1 package org.petify.backend.services; 2 3 import org.petify.backend.models.Achievement; 4 import org.petify.backend.models.AchievementCategory; 5 import org.petify.backend.models.ApplicationUser; 6 import org.petify.backend.models.UserAchievement; 7 import org.petify.backend.repository.AchievementRepository; 8 import org.petify.backend.repository.UserAchievementRepository; 9 import org.petify.backend.repository.UserRepository; 10 11 import lombok.extern.slf4j.Slf4j; 12 import org.springframework.beans.factory.annotation.Autowired; 13 import org.springframework.stereotype.Service; 14 import org.springframework.transaction.annotation.Transactional; 15 16 import java.time.LocalDateTime; 17 import java.util.HashMap; 18 import java.util.List; 19 import java.util.Map; 20 import java.util.Optional; 21 22 @Service 23 @Slf4j 24 public class AchievementService { 25 26 @Autowired 27 private AchievementRepository achievementRepository; 28 29 @Autowired 30 private UserAchievementRepository userAchievementRepository; 31 32 @Autowired 33 private UserRepository userRepository; 34 35 @Transactional(readOnly = true) 36 public List<UserAchievement> getUserAchievements(String username) { 37 ApplicationUser user = userRepository.findByUsername(username) 38 .orElseThrow(() -> new RuntimeException("User not found")); 39 40 return userAchievementRepository.findByUser(user); 41 } 42 43 @Transactional 44 public UserAchievement trackAchievementProgress(String username, Long achievementId, int progressIncrement) { 45 ApplicationUser user = userRepository.findByUsername(username) 46 .orElseThrow(() -> new RuntimeException("User not found")); 47 48 Achievement achievement = achievementRepository.findById(achievementId) 49 .orElseThrow(() -> new RuntimeException("Achievement not found")); 50 51 UserAchievement userAchievement = userAchievementRepository 52 .findByUserAndAchievementId(user, achievementId) 53 .orElseGet(() -> { 54 UserAchievement newAchievement = new UserAchievement(); 55 newAchievement.setUser(user); 56 newAchievement.setAchievement(achievement); 57 newAchievement.setCurrentProgress(0); 58 return newAchievement; 59 }); 60 61 if (userAchievement.getCompleted()) { 62 return userAchievement; 63 } 64 65 int newProgress = userAchievement.getCurrentProgress() + progressIncrement; 66 userAchievement.setCurrentProgress(newProgress); 67 68 if (newProgress >= achievement.getRequiredActions()) { 69 userAchievement.setCompleted(true); 70 userAchievement.setCompletionDate(LocalDateTime.now()); 71 72 user.setXpPoints(user.getXpPoints() + achievement.getXpReward()); 73 74 updateBadgeCounts(user, achievement.getCategory()); 75 76 updateUserLevel(user); 77 78 userRepository.save(user); 79 80 log.info("User {} completed achievement: {} (+{} XP)", 81 username, achievement.getName(), achievement.getXpReward()); 82 } 83 84 return userAchievementRepository.save(userAchievement); 85 } 86 87 @Transactional 88 public void trackLikeAchievements(String username) { 89 ApplicationUser user = userRepository.findByUsername(username) 90 .orElseThrow(() -> new RuntimeException("User not found")); 91 92 user.setLikesCount(user.getLikesCount() + 1); 93 userRepository.save(user); 94 95 List<Achievement> likeAchievements = achievementRepository.findByCategory(AchievementCategory.LIKES); 96 97 for (Achievement achievement : likeAchievements) { 98 trackAchievementProgress(username, achievement.getId(), 1); 99 } 100 log.info("Tracked like achievements for user {} - new like count: {}", 101 username, user.getLikesCount()); 102 } 103 104 @Transactional 105 public void trackSupportAchievements(String username) { 106 ApplicationUser user = userRepository.findByUsername(username) 107 .orElseThrow(() -> new RuntimeException("User not found")); 108 109 user.setSupportCount(user.getSupportCount() + 1); 110 userRepository.save(user); 111 112 List<Achievement> supportAchievements = achievementRepository.findByCategory(AchievementCategory.SUPPORT); 113 114 for (Achievement achievement : supportAchievements) { 115 trackAchievementProgress(username, achievement.getId(), 1); 116 } 117 118 log.info("Tracked support achievements for user {} - new support count: {}", 119 username, user.getSupportCount()); 120 } 121 122 @Transactional 123 public void trackProfileAchievementByName(String username, String achievementName) { 124 trackAchievementByNameAndCategory(username, achievementName, AchievementCategory.PROFILE); 125 } 126 127 private void updateBadgeCounts(ApplicationUser user, AchievementCategory category) { 128 switch (category) { 129 case LIKES: 130 break; 131 case SUPPORT: 132 break; 133 case ADOPTION: 134 user.setAdoptionCount(user.getAdoptionCount() + 1); 135 user.setBadgesCount(user.getBadgesCount() + 1); 136 break; 137 case BADGE: 138 case PROFILE: 139 case VOLUNTEER: 140 user.setBadgesCount(user.getBadgesCount() + 1); 141 break; 142 default: 143 break; 144 } 145 } 146 147 private void updateUserLevel(ApplicationUser user) { 148 int xp = user.getXpPoints(); 149 int newLevel = (xp / 100) + 1; 150 151 if (newLevel > user.getLevel()) { 152 int oldLevel = user.getLevel(); 153 user.setLevel(newLevel); 154 log.info("User {} leveled up from {} to {}! (Total XP: {})", 155 user.getUsername(), oldLevel, newLevel, xp); 156 } 157 } 158 159 public Map<String, Object> getUserLevelInfo(String username) { 160 ApplicationUser user = userRepository.findByUsername(username) 161 .orElseThrow(() -> new RuntimeException("User not found")); 162 163 Map<String, Object> levelInfo = new HashMap<>(); 164 levelInfo.put("level", user.getLevel()); 165 levelInfo.put("xpPoints", user.getXpPoints()); 166 levelInfo.put("xpToNextLevel", user.getXpToNextLevel()); 167 levelInfo.put("likesCount", user.getLikesCount()); 168 levelInfo.put("supportCount", user.getSupportCount()); 169 levelInfo.put("badgesCount", user.getBadgesCount()); 170 levelInfo.put("adoptionCount", user.getAdoptionCount()); 171 172 return levelInfo; 173 } 174 175 @Transactional 176 public void initializeUserAchievements(ApplicationUser user) { 177 List<Achievement> allAchievements = achievementRepository.findAll(); 178 179 for (Achievement achievement : allAchievements) { 180 UserAchievement userAchievement = new UserAchievement(); 181 userAchievement.setUser(user); 182 userAchievement.setAchievement(achievement); 183 userAchievement.setCurrentProgress(0); 184 userAchievement.setCompleted(false); 185 186 userAchievementRepository.save(userAchievement); 187 } 188 } 189 190 @Transactional 191 public void trackVolunteerAchievements(String username) { 192 List<Achievement> volunteerAchievements = achievementRepository.findByCategory(AchievementCategory.VOLUNTEER); 193 194 for (Achievement achievement : volunteerAchievements) { 195 trackAchievementProgress(username, achievement.getId(), 1); 196 } 197 } 198 199 @Transactional 200 public void trackVolunteerAchievementByName(String username, String achievementName) { 201 trackAchievementByNameAndCategory(username, achievementName, AchievementCategory.VOLUNTEER); 202 } 203 204 private void trackAchievementByNameAndCategory(String username, String achievementName, AchievementCategory category) { 205 try { 206 List<Achievement> achievements = achievementRepository.findByCategory(category); 207 208 Optional<Achievement> achievement = achievements.stream() 209 .filter(a -> a.getName().equals(achievementName)) 210 .findFirst(); 211 212 if (achievement.isPresent()) { 213 ApplicationUser user = userRepository.findByUsername(username) 214 .orElseThrow(() -> new RuntimeException("User not found")); 215 216 Optional<UserAchievement> existingUserAchievement = userAchievementRepository 217 .findByUserAndAchievementId(user, achievement.get().getId()); 218 219 if (existingUserAchievement.isEmpty() || !existingUserAchievement.get().getCompleted()) { 220 trackAchievementProgress(username, achievement.get().getId(), 1); 221 } 222 } else { 223 log.warn("Achievement with name '{}' not found in {} category", achievementName, category); 224 } 225 } catch (Exception e) { 226 log.error("Error tracking {} achievement '{}' for user {}: {}", 227 category, achievementName, username, e.getMessage()); 228 } 229 } 230 231 @Transactional 232 public void trackAdoptionAchievements(String username) { 233 List<Achievement> adoptionAchievements = achievementRepository.findByCategory(AchievementCategory.ADOPTION); 234 235 for (Achievement achievement : adoptionAchievements) { 236 trackAchievementProgress(username, achievement.getId(), 1); 237 } 238 } 239 240 @Transactional 241 public void addExperiencePointsForDonation(String username, int xpPoints) { 242 ApplicationUser user = userRepository.findByUsername(username) 243 .orElseThrow(() -> new RuntimeException("User not found")); 244 245 user.setXpPoints(user.getXpPoints() + xpPoints); 246 updateUserLevel(user); 247 userRepository.save(user); 248 249 log.info("Added {} XP to user {} for donation (Total XP: {})", 250 xpPoints, username, user.getXpPoints()); 251 } 252 }