Skip to content

Commit 6d55568

Browse files
committed
feat : 연속 커밋 축하 알림 구현 (#81)
1 parent 2dd13be commit 6d55568

File tree

6 files changed

+99
-41
lines changed

6 files changed

+99
-41
lines changed

src/main/java/cmf/commitField/domain/commit/scheduler/CommitScheduler.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package cmf.commitField.domain.commit.scheduler;
22

33
import cmf.commitField.domain.commit.totalCommit.service.TotalCommitService;
4+
import cmf.commitField.domain.noti.noti.entity.NotiDetailType;
5+
import cmf.commitField.domain.noti.noti.entity.NotiType;
6+
import cmf.commitField.domain.noti.noti.event.NotiEvent;
7+
import cmf.commitField.domain.noti.noti.service.CommitSteakNotiService;
8+
import cmf.commitField.domain.noti.noti.service.NotiService;
49
import cmf.commitField.domain.user.entity.User;
510
import cmf.commitField.domain.user.repository.UserRepository;
611
import lombok.RequiredArgsConstructor;
@@ -25,6 +30,8 @@ public class CommitScheduler {
2530
private final StringRedisTemplate redisTemplate;
2631
private final AtomicInteger counter = new AtomicInteger(0);
2732
private final SimpMessagingTemplate messagingTemplate;
33+
private final NotiService notiService;
34+
private final CommitSteakNotiService commitSteakNotiService;
2835

2936
private final ApplicationEventPublisher eventPublisher;
3037

@@ -86,6 +93,7 @@ private void processUserCommit(String username) {
8693
updateTotalCommit = totalCommitService.getTotalCommitCount(
8794
username
8895
).getTotalCommitContributions();
96+
int currentStreakCommit = totalCommitService.getTotalCommitCount(username).getCurrentStreakDays();
8997

9098
newCommitCount = updateTotalCommit - currentCommit; // 새로 추가된 커밋 수
9199

@@ -101,6 +109,9 @@ private void processUserCommit(String username) {
101109

102110
CommitUpdateEvent event = new CommitUpdateEvent(this, username, newCommitCount);
103111
eventPublisher.publishEvent(event); // 이벤트 발생
112+
113+
commitSteakNotiService.checkAndCreateSteakNoti(user, currentStreakCommit);
114+
104115
System.out.println("CommitCreatedEvent published for user: " + username);
105116
} else if(newCommitCount < 0) {
106117
// newCommitCount에 문제가 있을 경우 문제 상황 / 데이터 동기화 필요. db 갱신.
Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
11
package cmf.commitField.domain.noti.noti.entity;
22

3+
import java.text.MessageFormat;
4+
import java.util.Arrays;
5+
36
public enum NotiDetailType {
4-
// 업적
5-
RANK_UP, // 랭킹 상승
6-
ACHIEVEMENT_COMPLETED, // 업적 달성
7+
ACHIEVEMENT_COMPLETED("🎉 {0}님이 [{1}] 업적을 달성했습니다!", new String[]{"nickname", "achievementName"}),
8+
STREAK_CONTINUED("🔥 {0}님의 연속 커밋이 {1}일째 이어지고 있습니다!", new String[]{"nickname", "days"}),
9+
STREAK_BROKEN("😢 {0}님의 연속 커밋 기록이 끊겼습니다.", new String[]{"nickname"}),
10+
SEASON_START("🚀 새로운 [{0}] 시즌이 시작되었습니다! 랭킹 경쟁을 준비하세요!", new String[]{"seasonName"}),
11+
RANK_UP("📈 축하합니다! {0}님의 랭킹이 {1}(으)로 상승했습니다! 🎊", new String[]{"nickname", "tier"}),
12+
NOTICE_CREATED("📢 공지사항이 있습니다: {0}", new String[]{"noticeTitle"});
13+
14+
private final String template;
15+
private final String[] paramNames;
16+
17+
NotiDetailType(String template, String[] paramNames) {
18+
this.template = template;
19+
this.paramNames = paramNames;
20+
}
721

8-
// 연속
9-
STREAK_CONTINUED, // 연속 커밋 이어짐
10-
STREAK_BROKEN, // 연속 커밋 끊김
22+
public String getTemplate() {
23+
return template;
24+
}
1125

12-
NOTICE_CREATED, // 시즌
13-
SEASON_START // 시즌 시작
26+
public String[] getParamNames() {
27+
return paramNames;
28+
}
29+
30+
public String formatMessage(Object... params) {
31+
if (paramNames.length != params.length) {
32+
throw new IllegalArgumentException("🚨 잘못된 파라미터 개수! 필요: " +
33+
Arrays.toString(paramNames) + ", 제공됨: " + Arrays.toString(params));
34+
}
35+
return MessageFormat.format(template, params);
36+
}
1437
}
38+
39+

src/main/java/cmf/commitField/domain/noti/noti/entity/NotiMessageTemplates.java

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/main/java/cmf/commitField/domain/noti/noti/repository/NotiRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ public interface NotiRepository extends JpaRepository<Noti, Long> {
2424

2525
// 최근 10일 내 동일한 커밋 부재 알림이 있는지 확인
2626
boolean existsByReceiverAndTypeCodeAndType2CodeAndCreatedAtAfter(User receiver, NotiType type, NotiDetailType detailType, LocalDateTime after);
27+
28+
boolean existsByReceiverAndType2CodeAndCreatedAtAfter(User receiver, NotiDetailType notiDetailType, LocalDateTime todayStart);
2729
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,65 @@
11
package cmf.commitField.domain.noti.noti.service;
22

3+
import cmf.commitField.domain.commit.totalCommit.service.TotalCommitService;
4+
import cmf.commitField.domain.noti.noti.entity.NotiDetailType;
5+
import cmf.commitField.domain.noti.noti.entity.NotiType;
6+
import cmf.commitField.domain.noti.noti.repository.NotiRepository;
7+
import cmf.commitField.domain.user.entity.User;
8+
import cmf.commitField.domain.user.repository.UserRepository;
9+
import cmf.commitField.domain.user.service.UserService;
10+
import cmf.commitField.global.error.ErrorCode;
11+
import cmf.commitField.global.exception.CustomException;
312
import lombok.RequiredArgsConstructor;
413
import lombok.extern.slf4j.Slf4j;
14+
import org.springframework.scheduling.annotation.Scheduled;
15+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
16+
import org.springframework.security.oauth2.core.user.OAuth2User;
517
import org.springframework.stereotype.Service;
18+
import org.springframework.transaction.annotation.Transactional;
19+
20+
import java.time.LocalDate;
21+
import java.time.LocalDateTime;
22+
import java.util.List;
623

724
@Service
825
@RequiredArgsConstructor
926
@Slf4j
1027
public class CommitSteakNotiService {
28+
private final UserRepository userRepository;
29+
private final TotalCommitService totalCommitService;
1130
private final NotiService notiService;
31+
private final NotiRepository notiRepository;
32+
33+
// 매일 10시 실행
34+
@Scheduled(cron = "0 0 10 * * *")
35+
@Transactional
36+
public void sendCommitSteakNoti() {
37+
List<User> users = userRepository.findAll();
38+
39+
for (User user : users) {
40+
int currentStreakCommit = totalCommitService.getTotalCommitCount(user.getUsername()).getCurrentStreakDays();
41+
checkAndCreateSteakNoti(user, currentStreakCommit);
42+
}
43+
}
44+
45+
public void checkAndCreateSteakNoti(User user, int currentStreakCommit) {
46+
boolean alreadyNotified = notiRepository.existsByReceiverAndType2CodeAndCreatedAtAfter(
47+
user, NotiDetailType.STREAK_CONTINUED, LocalDate.now().atStartOfDay()
48+
);
49+
50+
log.info("알림 상세 타입: {}", NotiDetailType.STREAK_CONTINUED.name());
51+
52+
if (shouldNotify(currentStreakCommit) && !alreadyNotified) {
53+
log.info("🔍 연속 커밋 축하 알림 User: {}, Streak: {}", user.getUsername(), currentStreakCommit);
54+
notiService.createStreakCommitNoti(user, String.valueOf(currentStreakCommit));
55+
}
56+
}
57+
1258

59+
/**
60+
* 특정 연속 커밋 횟수에 도달했는지 확인
61+
*/
62+
public boolean shouldNotify(int streak) {
63+
return streak == 3 || (streak % 10 == 0);
64+
}
1365
}

src/main/java/cmf/commitField/domain/noti/noti/service/NotiService.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
import cmf.commitField.domain.noti.noti.dto.NotiDto;
44
import cmf.commitField.domain.noti.noti.entity.Noti;
55
import cmf.commitField.domain.noti.noti.entity.NotiDetailType;
6-
import cmf.commitField.domain.noti.noti.entity.NotiMessageTemplates;
76
import cmf.commitField.domain.noti.noti.entity.NotiType;
87
import cmf.commitField.domain.noti.noti.event.NotiEvent;
98
import cmf.commitField.domain.noti.noti.repository.NotiRepository;
10-
import cmf.commitField.domain.season.entity.Rank;
119
import cmf.commitField.domain.season.entity.Season;
1210
import cmf.commitField.domain.user.entity.User;
13-
import cmf.commitField.domain.user.repository.UserRepository;
1411
import cmf.commitField.global.error.ErrorCode;
1512
import cmf.commitField.global.exception.CustomException;
1613
import lombok.RequiredArgsConstructor;
@@ -19,7 +16,6 @@
1916
import org.springframework.stereotype.Service;
2017
import org.springframework.transaction.annotation.Transactional;
2118

22-
import java.text.MessageFormat;
2319
import java.util.ArrayList;
2420
import java.util.List;
2521

@@ -29,21 +25,13 @@
2925
@Slf4j
3026
public class NotiService {
3127
private final NotiRepository notiRepository;
32-
private final UserRepository userRepository;
3328
private final ApplicationEventPublisher eventPublisher;
3429

35-
// 알림 메시지 생성
36-
public static String generateMessage(NotiDetailType type, Object... params) {
37-
String template = NotiMessageTemplates.getTemplate(type);
38-
String message = MessageFormat.format(template, params); // params 배열을 그대로 전달
39-
return message;
40-
}
41-
4230
// 알림 생성
4331
@Transactional
4432
public void createNoti(User receiver, NotiType notiType, NotiDetailType notiDetailType, Long relId, String relTypeCode, Object... params) {
4533
// 메시지 생성
46-
String message = NotiService.generateMessage(notiDetailType, params);
34+
String message = notiDetailType.formatMessage(params);
4735

4836
// 알림 엔티티 생성
4937
Noti noti = Noti.builder()

0 commit comments

Comments
 (0)