fix(stars): keep favorites data in sync

This commit is contained in:
yun-zhi-ztl 2026-03-14 19:51:06 +08:00
parent 05c4842ac5
commit a1083412e1
3 changed files with 111 additions and 4 deletions

View file

@ -7,6 +7,7 @@ import com.iflytek.skillhub.domain.skill.SkillVersion;
import com.iflytek.skillhub.domain.skill.SkillVersionRepository;
import com.iflytek.skillhub.domain.social.SkillStarRepository;
import com.iflytek.skillhub.dto.SkillSummaryResponse;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.data.domain.PageRequest;
@ -19,6 +20,7 @@ import java.util.stream.Collectors;
@Service
public class MySkillAppService {
private static final int STAR_PAGE_SIZE = 200;
private final SkillRepository skillRepository;
private final NamespaceRepository namespaceRepository;
@ -68,10 +70,7 @@ public class MySkillAppService {
}
public List<SkillSummaryResponse> listMyStars(String userId) {
List<com.iflytek.skillhub.domain.social.SkillStar> stars = skillStarRepository.findByUserId(
userId,
PageRequest.of(0, 200)
).getContent();
List<com.iflytek.skillhub.domain.social.SkillStar> stars = loadAllStars(userId);
List<Long> skillIds = stars.stream()
.map(com.iflytek.skillhub.domain.social.SkillStar::getSkillId)
@ -111,6 +110,24 @@ public class MySkillAppService {
.toList();
}
private List<com.iflytek.skillhub.domain.social.SkillStar> loadAllStars(String userId) {
List<com.iflytek.skillhub.domain.social.SkillStar> stars = new java.util.ArrayList<>();
int pageNumber = 0;
while (true) {
Page<com.iflytek.skillhub.domain.social.SkillStar> page = skillStarRepository.findByUserId(
userId,
PageRequest.of(pageNumber, STAR_PAGE_SIZE)
);
stars.addAll(page.getContent());
if (!page.hasNext()) {
return stars;
}
pageNumber++;
}
}
private SkillSummaryResponse toSummaryResponse(
Skill skill,
Map<Long, SkillVersion> versionsById,

View file

@ -0,0 +1,89 @@
package com.iflytek.skillhub.service;
import com.iflytek.skillhub.domain.namespace.Namespace;
import com.iflytek.skillhub.domain.namespace.NamespaceRepository;
import com.iflytek.skillhub.domain.skill.Skill;
import com.iflytek.skillhub.domain.skill.SkillRepository;
import com.iflytek.skillhub.domain.skill.SkillVersion;
import com.iflytek.skillhub.domain.skill.SkillVersionRepository;
import com.iflytek.skillhub.domain.skill.SkillVisibility;
import com.iflytek.skillhub.domain.social.SkillStar;
import com.iflytek.skillhub.domain.social.SkillStarRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.util.ReflectionTestUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
@ExtendWith(MockitoExtension.class)
class MySkillAppServiceTest {
@Mock
private SkillRepository skillRepository;
@Mock
private NamespaceRepository namespaceRepository;
@Mock
private SkillVersionRepository skillVersionRepository;
@Mock
private SkillStarRepository skillStarRepository;
private MySkillAppService service;
@BeforeEach
void setUp() {
service = new MySkillAppService(skillRepository, namespaceRepository, skillVersionRepository, skillStarRepository);
}
@Test
void listMyStars_loadsAllPagesOfStars() {
SkillStar firstStar = new SkillStar(1L, "user-1");
ReflectionTestUtils.setField(firstStar, "createdAt", LocalDateTime.of(2026, 3, 14, 10, 0));
SkillStar secondStar = new SkillStar(2L, "user-1");
ReflectionTestUtils.setField(secondStar, "createdAt", LocalDateTime.of(2026, 3, 14, 11, 0));
given(skillStarRepository.findByUserId("user-1", PageRequest.of(0, 200)))
.willReturn(new PageImpl<>(List.of(firstStar), PageRequest.of(0, 200), 201));
given(skillStarRepository.findByUserId("user-1", PageRequest.of(1, 200)))
.willReturn(new PageImpl<>(List.of(secondStar), PageRequest.of(1, 200), 201));
Skill firstSkill = new Skill(1L, "first-skill", "user-1", SkillVisibility.PUBLIC);
firstSkill.setDisplayName("First Skill");
firstSkill.setSummary("first summary");
ReflectionTestUtils.setField(firstSkill, "id", 1L);
ReflectionTestUtils.setField(firstSkill, "starCount", 1);
ReflectionTestUtils.setField(firstSkill, "namespaceId", 101L);
ReflectionTestUtils.setField(firstSkill, "updatedAt", LocalDateTime.of(2026, 3, 14, 10, 0));
Skill secondSkill = new Skill(2L, "second-skill", "user-1", SkillVisibility.PUBLIC);
secondSkill.setDisplayName("Second Skill");
secondSkill.setSummary("second summary");
ReflectionTestUtils.setField(secondSkill, "id", 2L);
ReflectionTestUtils.setField(secondSkill, "starCount", 2);
ReflectionTestUtils.setField(secondSkill, "namespaceId", 101L);
ReflectionTestUtils.setField(secondSkill, "updatedAt", LocalDateTime.of(2026, 3, 14, 11, 0));
given(skillRepository.findByIdIn(List.of(1L, 2L))).willReturn(List.of(firstSkill, secondSkill));
given(namespaceRepository.findByIdIn(List.of(101L))).willReturn(List.of(new Namespace("team-ai", "Team AI", "user-1")));
var stars = service.listMyStars("user-1");
assertThat(stars).hasSize(2);
assertThat(stars.get(0).slug()).isEqualTo("second-skill");
assertThat(stars.get(1).slug()).isEqualTo("first-skill");
}
}

View file

@ -47,6 +47,7 @@ export function useToggleStar(skillId: number) {
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['skills', skillId, 'star'] })
queryClient.invalidateQueries({ queryKey: ['skills'] })
queryClient.invalidateQueries({ queryKey: ['skills', 'stars'] })
},
})
}