From 2ebccb40f5e4681f2558a7b606364e02bde6d22b Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Mon, 11 Aug 2025 20:15:49 +0800 Subject: [PATCH] feat: add pioneer medal dto --- .../com/openisle/dto/PioneerMedalDto.java | 10 ++++++++++ .../java/com/openisle/model/MedalType.java | 3 ++- .../openisle/repository/UserRepository.java | 2 ++ .../com/openisle/service/MedalService.java | 19 +++++++++++++++++++ .../openisle/service/MedalServiceTest.java | 7 ++++++- frontend_nuxt/components/AchievementList.vue | 1 + frontend_nuxt/utils/medal.js | 1 + 7 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 backend/src/main/java/com/openisle/dto/PioneerMedalDto.java diff --git a/backend/src/main/java/com/openisle/dto/PioneerMedalDto.java b/backend/src/main/java/com/openisle/dto/PioneerMedalDto.java new file mode 100644 index 000000000..bd04f93d7 --- /dev/null +++ b/backend/src/main/java/com/openisle/dto/PioneerMedalDto.java @@ -0,0 +1,10 @@ +package com.openisle.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PioneerMedalDto extends MedalDto { + private long rank; +} diff --git a/backend/src/main/java/com/openisle/model/MedalType.java b/backend/src/main/java/com/openisle/model/MedalType.java index d6c31bb7d..00553511d 100644 --- a/backend/src/main/java/com/openisle/model/MedalType.java +++ b/backend/src/main/java/com/openisle/model/MedalType.java @@ -4,5 +4,6 @@ public enum MedalType { COMMENT, POST, CONTRIBUTOR, - SEED + SEED, + PIONEER } diff --git a/backend/src/main/java/com/openisle/repository/UserRepository.java b/backend/src/main/java/com/openisle/repository/UserRepository.java index ddd25c661..64b7c88ee 100644 --- a/backend/src/main/java/com/openisle/repository/UserRepository.java +++ b/backend/src/main/java/com/openisle/repository/UserRepository.java @@ -2,6 +2,7 @@ package com.openisle.repository; import org.springframework.data.jpa.repository.JpaRepository; import com.openisle.model.User; +import java.time.LocalDateTime; import java.util.Optional; public interface UserRepository extends JpaRepository { @@ -10,4 +11,5 @@ public interface UserRepository extends JpaRepository { java.util.List findByUsernameContainingIgnoreCase(String keyword); java.util.List findByRole(com.openisle.model.Role role); long countByExperienceGreaterThanEqual(int experience); + long countByCreatedAtBefore(LocalDateTime createdAt); } diff --git a/backend/src/main/java/com/openisle/service/MedalService.java b/backend/src/main/java/com/openisle/service/MedalService.java index ba64923fc..daa4acc31 100644 --- a/backend/src/main/java/com/openisle/service/MedalService.java +++ b/backend/src/main/java/com/openisle/service/MedalService.java @@ -5,6 +5,7 @@ import com.openisle.dto.ContributorMedalDto; import com.openisle.dto.MedalDto; import com.openisle.dto.PostMedalDto; import com.openisle.dto.SeedUserMedalDto; +import com.openisle.dto.PioneerMedalDto; import com.openisle.model.MedalType; import com.openisle.model.User; import com.openisle.repository.CommentRepository; @@ -24,6 +25,7 @@ public class MedalService { private static final long POST_TARGET = 100; private static final LocalDateTime SEED_USER_DEADLINE = LocalDateTime.of(2025, 9, 16, 0, 0); private static final long CONTRIBUTION_TARGET = 1; + private static final long PIONEER_LIMIT = 1000; private final CommentRepository commentRepository; private final PostRepository postRepository; @@ -102,6 +104,21 @@ public class MedalService { } seedUserMedal.setSelected(selected == MedalType.SEED); medals.add(seedUserMedal); + + PioneerMedalDto pioneerMedal = new PioneerMedalDto(); + pioneerMedal.setIcon("https://openisle-1307107697.cos.ap-guangzhou.myqcloud.com/assert/icons/achi_pioneer.png"); + pioneerMedal.setTitle("开山鼻祖"); + pioneerMedal.setDescription("前1000位加入的用户"); + pioneerMedal.setType(MedalType.PIONEER); + if (user != null) { + long rank = userRepository.countByCreatedAtBefore(user.getCreatedAt()) + 1; + pioneerMedal.setRank(rank); + pioneerMedal.setCompleted(rank <= PIONEER_LIMIT); + } else { + pioneerMedal.setCompleted(false); + } + pioneerMedal.setSelected(selected == MedalType.PIONEER); + medals.add(pioneerMedal); if (user != null && selected == null) { for (MedalDto medal : medals) { if (medal.isCompleted()) { @@ -126,6 +143,8 @@ public class MedalService { user.setDisplayMedal(MedalType.POST); } else if (contributorService.getContributionLines(user.getUsername()) >= CONTRIBUTION_TARGET) { user.setDisplayMedal(MedalType.CONTRIBUTOR); + } else if (userRepository.countByCreatedAtBefore(user.getCreatedAt()) < PIONEER_LIMIT) { + user.setDisplayMedal(MedalType.PIONEER); } else if (user.getCreatedAt().isBefore(SEED_USER_DEADLINE)) { user.setDisplayMedal(MedalType.SEED); } diff --git a/backend/src/test/java/com/openisle/service/MedalServiceTest.java b/backend/src/test/java/com/openisle/service/MedalServiceTest.java index 49dfc4268..a4a10a56d 100644 --- a/backend/src/test/java/com/openisle/service/MedalServiceTest.java +++ b/backend/src/test/java/com/openisle/service/MedalServiceTest.java @@ -27,7 +27,7 @@ class MedalServiceTest { List medals = service.getMedals(null); medals.forEach(m -> assertFalse(m.isCompleted())); - assertEquals(4, medals.size()); + assertEquals(5, medals.size()); } @Test @@ -40,6 +40,7 @@ class MedalServiceTest { when(commentRepo.countByAuthor_Id(1L)).thenReturn(120L); when(postRepo.countByAuthor_Id(1L)).thenReturn(80L); when(contributorService.getContributionLines(anyString())).thenReturn(0L); + when(userRepo.countByCreatedAtBefore(any())).thenReturn(50L); User user = new User(); user.setId(1L); user.setCreatedAt(LocalDateTime.of(2025, 9, 15, 0, 0)); @@ -56,6 +57,8 @@ class MedalServiceTest { assertFalse(medals.stream().filter(m -> m.getType() == MedalType.POST).findFirst().orElseThrow().isSelected()); assertTrue(medals.stream().filter(m -> m.getType() == MedalType.SEED).findFirst().orElseThrow().isCompleted()); assertFalse(medals.stream().filter(m -> m.getType() == MedalType.SEED).findFirst().orElseThrow().isSelected()); + assertTrue(medals.stream().filter(m -> m.getType() == MedalType.PIONEER).findFirst().orElseThrow().isCompleted()); + assertFalse(medals.stream().filter(m -> m.getType() == MedalType.PIONEER).findFirst().orElseThrow().isSelected()); verify(userRepo).save(user); } @@ -69,6 +72,7 @@ class MedalServiceTest { when(commentRepo.countByAuthor_Id(1L)).thenReturn(120L); when(postRepo.countByAuthor_Id(1L)).thenReturn(0L); when(contributorService.getContributionLines(anyString())).thenReturn(0L); + when(userRepo.countByCreatedAtBefore(any())).thenReturn(0L); User user = new User(); user.setId(1L); user.setCreatedAt(LocalDateTime.of(2025, 9, 15, 0, 0)); @@ -90,6 +94,7 @@ class MedalServiceTest { when(commentRepo.countByAuthor_Id(1L)).thenReturn(10L); when(postRepo.countByAuthor_Id(1L)).thenReturn(0L); when(contributorService.getContributionLines(anyString())).thenReturn(0L); + when(userRepo.countByCreatedAtBefore(any())).thenReturn(0L); User user = new User(); user.setId(1L); user.setCreatedAt(LocalDateTime.of(2025, 9, 15, 0, 0)); diff --git a/frontend_nuxt/components/AchievementList.vue b/frontend_nuxt/components/AchievementList.vue index 2039401a0..4131339f1 100644 --- a/frontend_nuxt/components/AchievementList.vue +++ b/frontend_nuxt/components/AchievementList.vue @@ -29,6 +29,7 @@ + diff --git a/frontend_nuxt/utils/medal.js b/frontend_nuxt/utils/medal.js index 7406693c3..2f61e537d 100644 --- a/frontend_nuxt/utils/medal.js +++ b/frontend_nuxt/utils/medal.js @@ -3,6 +3,7 @@ export const medalTitles = { POST: '发帖达人', SEED: '种子用户', CONTRIBUTOR: '贡献者', + PIONEER: '开山鼻祖', } export function getMedalTitle(type) {