Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,43 +1,102 @@
package cmf.commitField.domain.commit.scheduler;

import cmf.commitField.domain.commit.sinceCommit.service.CommitCacheService;
import cmf.commitField.domain.commit.sinceCommit.service.GithubService;
import cmf.commitField.domain.commit.totalCommit.service.TotalCommitService;
import cmf.commitField.domain.redpanda.RedpandaProducer;
import cmf.commitField.domain.user.entity.User;
import cmf.commitField.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Service
@RequiredArgsConstructor
public class CommitScheduler {
private final GithubService githubService;
private final TotalCommitService totalCommitService;
private final CommitCacheService commitCacheService;
private final RedpandaProducer redpandaProducer;
private final UserRepository userRepository;
private final StringRedisTemplate redisTemplate;
private final AtomicInteger counter = new AtomicInteger(0);

@Scheduled(fixedRate = 60000) // 1분마다 실행
public void updateUserCommits() {
log.info("🔍 updateUserCommits 실행중");
List<User> activeUsers = userRepository.findAll(); // 💫 변경 필요, 차후 active 상태인 user만 찾게끔 변경해야 함.
int count = counter.incrementAndGet();

log.info("🔍 Active User Count: {}", activeUsers.size());
if (count % 10 == 0) {
List<User> allUsers = userRepository.findAll();
log.info("🔍 All User Count: {}", allUsers.size());

for (User user : activeUsers) {
Integer cachedCount = commitCacheService.getCachedCommitCount(user.getUsername());
int newCommitCount = githubService.getUserCommitCount(user.getUsername());

log.info("🔍 User: {}, Commit Count: {}", user.getUsername(), newCommitCount);
for (User user : allUsers) {
processUserCommit(user);
}
} else {
Set<String> activeUsers = redisTemplate.keys("commit_active:*");
log.info("🔍 Active User Count: {}", activeUsers.size());

if (cachedCount == null || cachedCount != newCommitCount) { // 변화가 있을 때만 처리
commitCacheService.updateCachedCommitCount(user.getUsername(), newCommitCount);
redpandaProducer.sendCommitUpdate(user.getUsername(), newCommitCount);
for (String key : activeUsers) {
String username = key.replace("commit_active:", "");
User user = userRepository.findByUsername(username).orElse(null);
if (user != null) {
processUserCommit(user);
}
}
}
}

// 🔹 유저 커밋 검사 및 반영
private void processUserCommit(User user) {
// Redis에서 lastCommitted 값 가져오기
String redisKey = "commit_last:" + user.getUsername();
String lastCommittedStr = redisTemplate.opsForValue().get(redisKey);
LocalDateTime lastCommitted;
if(lastCommittedStr != null){
lastCommitted=LocalDateTime.parse(lastCommittedStr);
}else{
user.setLastCommitted(LocalDateTime.now()); // 레디스에 저장되어있지 않았다면 등록 시점에 lastCommitted를 갱신
lastCommitted=user.getLastCommitted(); // Redis에 없으면 DB값 사용;
}

// 현재 커밋 개수 조회
long currentCommitCount = totalCommitService.getSeasonCommits(
user.getUsername(),
lastCommitted, // 🚀 Redis에 저장된 lastCommitted 기준으로 조회
LocalDateTime.now()
).getTotalCommitContributions();

// Redis에서 이전 커밋 개수 가져오기
Integer previousCommitCount = commitCacheService.getCachedCommitCount(user.getUsername());
long newCommitCount = previousCommitCount == null ? 0 : (currentCommitCount - previousCommitCount);

if (newCommitCount > 0) {
updateCommitData(user, currentCommitCount, newCommitCount);
}

log.info("🔍 User: {}, New Commits: {}, Total Commits: {}", user.getUsername(), newCommitCount, currentCommitCount);
}

// 🔹 새 커밋이 있으면 데이터 업데이트
private void updateCommitData(User user, long currentCommitCount, long newCommitCount) {
// 1️⃣ Redis에 lastCommitted 업데이트 (3시간 TTL)
String redisKey = "commit_last:" + user.getUsername();
redisTemplate.opsForValue().set(redisKey, LocalDateTime.now().toString(), 3, TimeUnit.HOURS);

// 2️⃣ Redis에 최신 커밋 개수 저장 (3시간 동안 유지)
commitCacheService.updateCachedCommitCount(user.getUsername(), currentCommitCount);

// 3️⃣ 메시지 큐 전송
redpandaProducer.sendCommitUpdate(user.getUsername(), newCommitCount);

log.info("✅ 커밋 반영 완료 - User: {}, New Commits: {}", user.getUsername(), newCommitCount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@ public class CommitCacheService {
private final StringRedisTemplate redisTemplate;

public Integer getCachedCommitCount(String username) {
log.info("Redis Template: {}", redisTemplate);
String key = "commit:" + username; // Redis 키 생성 (ex: commit:hongildong)
String key = "commit_active:" + username; // Redis 키 생성 (ex: commit:hongildong)
String value = redisTemplate.opsForValue().get(key); // Redis에서 값 가져오기
return value != null ? Integer.parseInt(value) : null; // 값이 있으면 정수 변환, 없으면 null 반환

if (value != null) {
log.info("✅ Redis Hit - {} : {}", key, value);
return Integer.parseInt(value);
} else {
log.info("❌ Redis Miss - {}", key);
return null;
}
}

public void updateCachedCommitCount(String username, int count) {
String key = "commit:" + username;
redisTemplate.opsForValue().set(key, String.valueOf(count), Duration.ofHours(1)); // 1시간 캐싱
public void updateCachedCommitCount(String username, long count) {
String key = "commit_active:" + username;
redisTemplate.opsForValue().set(key, String.valueOf(count), Duration.ofHours(3)); // 3시간 캐싱
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ public class Noti extends BaseEntity {
// TODO: 알림이 연결된 객체 어떻게 처리할지 고민 필요.
// private String relTypeCode; // 알림이 연결된 실제 객체 유형
// private long relId; // 알림 객체의 Id

}
29 changes: 28 additions & 1 deletion src/main/java/cmf/commitField/domain/pet/entity/Pet.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.util.Random;

@Entity
@Getter
@Setter
Expand All @@ -17,11 +19,36 @@
@SuperBuilder
@Table(name = "pet")
public class Pet extends BaseEntity {
private int type; // 펫 타입 넘버, 현재 1~3까지 존재
private int type; // 펫 타입 넘버, 현재 0~2까지 존재
private String name;
private String imageUrl;
private int exp; // 펫 경험치

@Enumerated(EnumType.STRING) // DB에 저장될 때 String 형태로 저장됨
private Grow grow; // 성장 정도

public enum Grow {
EGG, HATCH, GROWN
}

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

public Pet(String name, User user){
Random random = new Random();
this.type = random.nextInt(3);
switch (type){ //정해진 알 타입에 따라 다른 url을 주입
case 0: this.imageUrl = "temp0";
break;
case 1: this.imageUrl = "temp1";
break;
case 2: this.imageUrl = "temp2";
break;
}
this.name = name;
this.exp = 0;
this.grow = Grow.EGG;
this.user = user;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Pet createPet(String name, MultipartFile imageFile, User user) throws IOE
imageUrl = s3Service.uploadFile(imageFile, "pet-images");
}
Random random = new Random();
Pet pet = new Pet(random.nextInt(3), name, imageUrl, user);
Pet pet = new Pet(name, user);
return petRepository.save(pet);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public void sendMessage(String message) {
}

// 커밋 업데이트 전송 메서드
public void sendCommitUpdate(String username, int commitCount) {
String message = String.format("{\"user\": \"%s\", \"commits\": %d}", username, commitCount);
public void sendCommitUpdate(String username, long commitCount) {
String message = String.format("{\"user\": \"%s\", \"update-commits\": %d}", username, commitCount);
kafkaTemplate.send(TOPIC, message);
System.out.println("📨 Sent commit update to Redpanda: " + message);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
public enum Rank {
SEED, // 씨앗
SPROUT, // 새싹
STEM, // 줄기
FLOWER, // 꽃
FRUIT, // 열매
TREE // 나무
Expand Down
23 changes: 22 additions & 1 deletion src/main/java/cmf/commitField/domain/user/entity/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import cmf.commitField.domain.chat.chatMessage.entity.ChatMsg;
import cmf.commitField.domain.chat.chatRoom.entity.ChatRoom;
import cmf.commitField.domain.chat.userChatRoom.entity.UserChatRoom;
import cmf.commitField.domain.pet.entity.Pet;
import cmf.commitField.global.jpa.BaseEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
Expand All @@ -11,6 +12,7 @@
import lombok.Setter;
import lombok.experimental.SuperBuilder;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -25,14 +27,15 @@ public class User extends BaseEntity {
private String email; // 이메일
private String nickname; // 닉네임
private String avatarUrl; //아바타
private LocalDateTime lastCommitted; // 마지막 커밋 시간

@Enumerated(EnumType.STRING) // DB에 저장될 때 String 형태로 저장됨
private Role role;

public enum Role {
USER, ADMIN
}
//추가

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private List<ChatRoom> chatRooms = new ArrayList<>();

Expand All @@ -42,4 +45,22 @@ public enum Role {
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private List<ChatMsg> chatMsgs = new ArrayList<>();

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private List<Pet> pets = new ArrayList<>();

public void addPets(Pet pet){
pets.add(pet);
}

public User(String username, String email, String nickname, String avatarUrl, List<ChatRoom> cr, List<UserChatRoom> ucr, List<ChatMsg> cmsg){
this.username=username;
this.email=email;
this.nickname=nickname;
this.avatarUrl=avatarUrl;
this.role = Role.USER;
this.chatRooms = cr;
this.userChatRooms = ucr;
this.chatMsgs = cmsg;
this.lastCommitted = LocalDateTime.now();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cmf.commitField.domain.user.service;

import cmf.commitField.domain.commit.sinceCommit.service.CommitCacheService;
import cmf.commitField.domain.pet.entity.Pet;
import cmf.commitField.domain.pet.repository.PetRepository;
import cmf.commitField.domain.user.entity.CustomOAuth2User;
Expand All @@ -14,13 +15,13 @@
import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;
import java.util.Random;

@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private final UserRepository userRepository;
private final PetRepository petRepository;
private final CommitCacheService commitCacheService;

@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) {
Expand Down Expand Up @@ -49,12 +50,16 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) {
} else {
//유저 정보가 db에 존재하지 않을 경우 회원가입 시킨다.
//유저 생성 및 펫 생성
user = new User(username, email, name, avatarUrl, User.Role.USER, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
user = new User(username, email, name, avatarUrl, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
userRepository.save(user);

Random random = new Random(); //펫 랜덤 생성
pet = new Pet(random.nextInt(3), "알알", "temp-Url", user); // 변경 필요
pet = new Pet("알알", user); // 변경 필요
petRepository.save(pet);

user.addPets(pet);

// 회원가입한 유저는 커밋 기록에 상관없이 Redis에 입력해둔다.
commitCacheService.updateCachedCommitCount(user.getUsername(),0);
}

return new CustomOAuth2User(oauthUser, user);
Expand Down