Skip to content

Commit de5ea3f

Browse files
authored
Merge pull request #1080 from Moadong/develop/be
2 parents fa6f2e2 + 7bb7517 commit de5ea3f

File tree

17 files changed

+278
-49
lines changed

17 files changed

+278
-49
lines changed

backend/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ dependencies {
6363

6464
implementation 'com.google.firebase:firebase-admin:9.7.0'
6565

66+
implementation 'org.javers:javers-spring-boot-starter-mongo:7.10.0'
6667
}
6768

6869
//전체 테스트

backend/src/main/java/moadong/club/entity/Club.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import moadong.club.payload.request.ClubRecruitmentInfoUpdateRequest;
1414
import moadong.global.exception.ErrorCode;
1515
import moadong.global.exception.RestApiException;
16+
import org.javers.core.metamodel.annotation.DiffIgnore;
1617
import org.springframework.data.annotation.Id;
1718
import org.springframework.data.annotation.Version;
1819
import org.springframework.data.domain.Persistable;
@@ -41,6 +42,7 @@ public class Club implements Persistable<String> {
4142

4243
private String userId;
4344

45+
@DiffIgnore
4446
private Map<String, String> socialLinks;
4547

4648
@Field("recruitmentInformation")
@@ -146,9 +148,11 @@ public void updateRecruitmentStatus(ClubRecruitmentStatus clubRecruitmentStatus)
146148

147149
public void sendPushNotification(Message message) {
148150
try {
149-
FirebaseMessaging.getInstance().send(message);
151+
log.info("FCM 알림 전송 시작 - clubId: {}, clubName: {}", this.id, this.name);
152+
String messageId = FirebaseMessaging.getInstance().send(message);
153+
log.info("FCM 알림 전송 성공 - clubId: {}, messageId: {}", this.id, messageId);
150154
} catch (FirebaseMessagingException e) {
151-
log.error("FirebaseSendNotificationError: {}", e.getMessage());
155+
log.error("FCM 알림 전송 실패 - clubId: {}, error: {}", this.id, e.getMessage());
152156
}
153157
}
154158

backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import moadong.club.payload.request.ClubRecruitmentInfoUpdateRequest;
1515
import moadong.global.RegexConstants;
1616
import org.checkerframework.common.aliasing.qual.Unique;
17+
import org.javers.core.metamodel.annotation.DiffIgnore;
1718

1819
import java.time.Instant;
1920
import java.time.LocalDateTime;
@@ -26,15 +27,17 @@
2627
@Builder(toBuilder = true)
2728
public class ClubRecruitmentInformation {
2829

29-
@Id
30-
private String id;
30+
// @Id
31+
// private String id;
3132

3233
@Column(length = 1024)
3334
@Unique
35+
@DiffIgnore
3436
private String logo;
3537

3638
@Column(length = 1024)
3739
@Unique
40+
@DiffIgnore
3841
private String cover;
3942

4043
@Column(length = 30)
@@ -45,6 +48,7 @@ public class ClubRecruitmentInformation {
4548

4649
@Pattern(regexp = RegexConstants.PHONE_NUMBER, message = "전화번호 형식이 올바르지 않습니다.")
4750
@Column(length = 13)
51+
@DiffIgnore
4852
private String presidentTelephoneNumber;
4953

5054
private Instant recruitmentStart;
@@ -53,16 +57,20 @@ public class ClubRecruitmentInformation {
5357

5458
private String recruitmentTarget;
5559

60+
@DiffIgnore
5661
String externalApplicationUrl;
5762

63+
@DiffIgnore
5864
private List<String> feedImages;
5965

6066
private List<String> tags;
6167

6268
@Enumerated(EnumType.STRING)
6369
@NotNull
70+
@DiffIgnore
6471
private ClubRecruitmentStatus clubRecruitmentStatus;
6572

73+
@DiffIgnore
6674
private LocalDateTime lastModifiedDate;
6775

6876
public void updateLogo(String logo) {

backend/src/main/java/moadong/club/repository/ClubRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Optional;
55
import moadong.club.entity.Club;
66
import org.bson.types.ObjectId;
7+
import org.javers.spring.annotation.JaversSpringDataAuditable;
78
import org.springframework.data.mongodb.repository.MongoRepository;
89
import org.springframework.stereotype.Repository;
910

backend/src/main/java/moadong/club/service/ClubProfileService.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import moadong.global.util.ObjectIdConverter;
1515
import moadong.user.payload.CustomUserDetails;
1616
import org.bson.types.ObjectId;
17+
import org.javers.core.Javers;
1718
import org.springframework.stereotype.Service;
1819
import org.springframework.transaction.annotation.Transactional;
1920

@@ -23,27 +24,31 @@ public class ClubProfileService {
2324

2425
private final ClubRepository clubRepository;
2526
private final ClubSearchRepository clubSearchRepository;
27+
private final RecruitmentStateCalculator recruitmentStateCalculator;
28+
private final Javers javers;
2629

2730
@Transactional
2831
public void updateClubInfo(ClubInfoRequest request, CustomUserDetails user) {
2932
Club club = clubRepository.findClubByUserId(user.getId())
3033
.orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
3134
club.update(request);
32-
clubRepository.save(club);
35+
Club saved = clubRepository.save(club);
36+
javers.commit(user.getUsername(), saved);
3337
}
3438

3539
public void updateClubRecruitmentInfo(ClubRecruitmentInfoUpdateRequest request,
3640
CustomUserDetails user) {
3741
Club club = clubRepository.findClubByUserId(user.getId())
3842
.orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
3943
club.update(request);
40-
RecruitmentStateCalculator.calculate(
44+
recruitmentStateCalculator.calculate(
4145
club,
4246
club.getClubRecruitmentInformation().getRecruitmentStart(),
4347
club.getClubRecruitmentInformation().getRecruitmentEnd()
4448
);
4549
club.getClubRecruitmentInformation().updateLastModifiedDate();
46-
clubRepository.save(club);
50+
Club saved = clubRepository.save(club);
51+
javers.commit(user.getUsername(), saved);
4752
}
4853

4954
public ClubDetailedResponse getClubDetail(String clubId) {
@@ -57,3 +62,4 @@ public ClubDetailedResponse getClubDetail(String clubId) {
5762
return new ClubDetailedResponse(clubDetailedResult);
5863
}
5964
}
65+

backend/src/main/java/moadong/club/service/RecruitmentStateChecker.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
public class RecruitmentStateChecker {
2222

2323
private final ClubRepository clubRepository;
24+
private final RecruitmentStateCalculator recruitmentStateCalculator;
2425

2526
@Scheduled(fixedRate = 60 * 60 * 1000) // 1시간마다 실행
2627
public void performTask() {
@@ -32,9 +33,10 @@ public void performTask() {
3233
if (recruitInfo.getClubRecruitmentStatus() == ClubRecruitmentStatus.ALWAYS) {
3334
continue;
3435
}
35-
RecruitmentStateCalculator.calculate(club, recruitmentStartDate, recruitmentEndDate);
36+
recruitmentStateCalculator.calculate(club, recruitmentStartDate, recruitmentEndDate);
3637

3738
clubRepository.save(club);
3839
}
3940
}
4041
}
42+

backend/src/main/java/moadong/club/util/RecruitmentStateCalculator.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,21 @@
88

99
import com.google.firebase.messaging.Message;
1010
import com.google.firebase.messaging.Notification;
11+
import lombok.RequiredArgsConstructor;
1112
import moadong.club.entity.Club;
1213
import moadong.club.entity.ClubRecruitmentInformation;
1314
import moadong.club.enums.ClubRecruitmentStatus;
15+
import moadong.fcm.util.FcmTopicResolver;
16+
import org.springframework.stereotype.Component;
1417

18+
@Component
19+
@RequiredArgsConstructor
1520
public class RecruitmentStateCalculator {
1621
public static final int ALWAYS_RECRUIT_YEAR = 2999;
1722

18-
public static void calculate(Club club, ZonedDateTime recruitmentStartDate, ZonedDateTime recruitmentEndDate) {
23+
private final FcmTopicResolver fcmTopicResolver;
24+
25+
public void calculate(Club club, ZonedDateTime recruitmentStartDate, ZonedDateTime recruitmentEndDate) {
1926
ClubRecruitmentStatus oldStatus = club.getClubRecruitmentInformation().getClubRecruitmentStatus();
2027
ClubRecruitmentStatus newStatus = calculateRecruitmentStatus(recruitmentStartDate, recruitmentEndDate);
2128
club.updateRecruitmentStatus(newStatus);
@@ -50,7 +57,7 @@ public static ClubRecruitmentStatus calculateRecruitmentStatus(ZonedDateTime rec
5057
return ClubRecruitmentStatus.CLOSED;
5158
}
5259

53-
public static Message buildRecruitmentMessage(Club club, ClubRecruitmentStatus status) {
60+
public Message buildRecruitmentMessage(Club club, ClubRecruitmentStatus status) {
5461
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M월 d일 a h시 m분", Locale.KOREAN);
5562
ClubRecruitmentInformation info = club.getClubRecruitmentInformation();
5663

@@ -72,7 +79,8 @@ public static Message buildRecruitmentMessage(Club club, ClubRecruitmentStatus s
7279
.setTitle(club.getName())
7380
.setBody(bodyMessage)
7481
.build())
75-
.setTopic(club.getId())
82+
.setTopic(fcmTopicResolver.resolveTopic(club.getId()))
7683
.build();
7784
}
7885
}
86+

backend/src/main/java/moadong/fcm/service/FcmAsyncService.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.google.firebase.messaging.TopicManagementResponse;
77
import lombok.RequiredArgsConstructor;
88
import lombok.extern.slf4j.Slf4j;
9+
import moadong.fcm.util.FcmTopicResolver;
910
import moadong.global.exception.ErrorCode;
1011
import moadong.global.exception.RestApiException;
1112
import org.springframework.beans.factory.annotation.Value;
@@ -30,6 +31,8 @@ public class FcmAsyncService {
3031

3132
private final FirebaseMessaging firebaseMessaging;
3233

34+
private final FcmTopicResolver fcmTopicResolver;
35+
3336
@Value("${fcm.topic.timeout-seconds:5}")
3437
private int timeoutSeconds;
3538

@@ -40,14 +43,16 @@ public CompletableFuture<Void> updateSubscriptions(String token, Set<String> new
4043
// 새로운 동아리 구독
4144
if (!clubsToSubscribe.isEmpty()) {
4245
for (String clubId : clubsToSubscribe) {
43-
futures.add(firebaseMessaging.subscribeToTopicAsync(Collections.singletonList(token), clubId));
46+
String topic = fcmTopicResolver.resolveTopic(clubId);
47+
futures.add(firebaseMessaging.subscribeToTopicAsync(Collections.singletonList(token), topic));
4448
}
4549
}
4650

4751
// 더 이상 구독하지 않는 동아리 구독 해제
4852
if (!clubsToUnsubscribe.isEmpty()) {
4953
for (String clubId : clubsToUnsubscribe) {
50-
futures.add(firebaseMessaging.unsubscribeFromTopicAsync(Collections.singletonList(token), clubId));
54+
String topic = fcmTopicResolver.resolveTopic(clubId);
55+
futures.add(firebaseMessaging.unsubscribeFromTopicAsync(Collections.singletonList(token), topic));
5156
}
5257
}
5358

@@ -84,3 +89,4 @@ public CompletableFuture<Void> updateSubscriptions(String token, Set<String> new
8489
return CompletableFuture.completedFuture(null);
8590
}
8691
}
92+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package moadong.fcm.util;
2+
3+
import org.springframework.beans.factory.annotation.Value;
4+
import org.springframework.stereotype.Component;
5+
6+
@Component
7+
public class FcmTopicResolver {
8+
9+
@Value("${spring.profiles.active:prod}")
10+
private String activeProfile;
11+
12+
public String resolveTopic(String clubId) {
13+
if ("prod".equals(activeProfile)) {
14+
return clubId;
15+
}
16+
return activeProfile + "_" + clubId;
17+
}
18+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package moadong.global.config;
2+
3+
import org.javers.core.Javers;
4+
import org.javers.core.JaversBuilder;
5+
import org.javers.repository.mongo.MongoRepository;
6+
import org.javers.spring.auditable.AuthorProvider;
7+
import org.springframework.context.annotation.Bean;
8+
import org.springframework.context.annotation.Configuration;
9+
import org.springframework.data.mongodb.MongoDatabaseFactory;
10+
import org.springframework.security.core.context.SecurityContextHolder;
11+
import org.springframework.transaction.PlatformTransactionManager;
12+
13+
@Configuration
14+
public class JaversConfig {
15+
@Bean
16+
public AuthorProvider authorProvider() {
17+
return () -> {
18+
return SecurityContextHolder.getContext().getAuthentication().getName();
19+
};
20+
}
21+
22+
@Bean
23+
public MongoRepository javersMongoRepository(MongoDatabaseFactory dbFactory) {
24+
// MongoDatabaseFactory에서 database 객체를 꺼내서 Javers에 넘김
25+
return new MongoRepository(dbFactory.getMongoDatabase());
26+
}
27+
}

0 commit comments

Comments
 (0)