Skip to content

Commit 808d2ba

Browse files
authored
Merge pull request #90 from TU-NEBULA/feature/SCRUM-227
SCRUM 227 : 2D 그래프 스타 전체 조회 API 구현
2 parents 1d2c6ac + 8f813ac commit 808d2ba

16 files changed

+316
-59
lines changed

src/main/java/com/team_nebula/nebula/domain/category/entity/Category.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class Category extends BaseEntity {
2222
@Property(name = "name")
2323
private String name;
2424

25-
@Property("Deleted status")
25+
@Property("isDeletedStatus")
2626
private Boolean isDeletedStatus;
2727

2828
@Relationship(type = "BELONGS_TO", direction = Relationship.Direction.INCOMING)

src/main/java/com/team_nebula/nebula/domain/link/repository/LinkRepository.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,19 @@ WITH s1, s2, COLLECT(DISTINCT k.name) AS sharedKeywords, COUNT(DISTINCT k) AS sh
3636

3737

3838
@Query("""
39-
MATCH (u:UserNode)-[:CREATED]->(s:Star)
40-
WHERE u.userId = $userId AND (s.isDeletedStatus = false OR s.isDeletedStatus IS NULL)
41-
42-
MATCH (s)-[:LINKED]->(l:Link)<-[:LINKED]-(s2:Star)
39+
MATCH (u:UserNode {userId: $userId})-[:CREATED]->(s:Star)
40+
WHERE s.isDeletedStatus = false OR s.isDeletedStatus IS NULL
41+
42+
MATCH (s)-[:LINKED]-(l:Link)-[:LINKED]-(s2:Star)
4343
WHERE (u)-[:CREATED]->(s2) AND (s2.isDeletedStatus = false OR s2.isDeletedStatus IS NULL)
44-
45-
MATCH (s)-[:TAGGED]->(k:Keyword)<-[:TAGGED]-(s2)
46-
44+
45+
OPTIONAL MATCH (s)-[:TAGGED]->(k:Keyword)<-[:TAGGED]-(s2)
46+
4747
RETURN DISTINCT l.id AS linkId,
48-
l.linked_two_node_Id AS linkedNodeIdList,
49-
l.sharedKeywordNum AS sharedKeywordNum,
50-
COLLECT(DISTINCT k.name) AS sharedKeywords,
51-
l.similarityScore AS similarity
48+
l.linked_two_node_Id AS linkedNodeIdList,
49+
l.sharedKeywordNum AS sharedKeywordNum,
50+
COLLECT(DISTINCT k.name) AS sharedKeywords,
51+
l.similarityScore AS similarity
5252
""")
5353
List<GetLinkOneResponseDTO> findLinksByUserId(@Param("userId") Long userId);
5454

src/main/java/com/team_nebula/nebula/domain/star/api/StarV2Controller.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import com.team_nebula.nebula.domain.star.dto.request.CreateStarRequestDTO;
55
import com.team_nebula.nebula.domain.star.dto.response.AddBookMarkResponseDTO;
66
import com.team_nebula.nebula.domain.star.dto.response.CreateStarResponseDTO;
7+
import com.team_nebula.nebula.domain.star.dto.response.GetCategoryAndKeywordListDTO;
78
import com.team_nebula.nebula.domain.star.dto.response.PutStarResponseDTO;
89
import com.team_nebula.nebula.domain.star.service.StarCommandService;
10+
import com.team_nebula.nebula.domain.star.service.StarQueryService;
911
import com.team_nebula.nebula.global.annotation.AuthUser;
1012
import com.team_nebula.nebula.global.apipayload.ApiResponse;
1113
import io.swagger.v3.oas.annotations.Operation;
@@ -15,6 +17,8 @@
1517
import org.springframework.web.bind.annotation.*;
1618
import org.springframework.web.multipart.MultipartFile;
1719

20+
import java.util.Collections;
21+
import java.util.List;
1822
import java.util.UUID;
1923

2024
@RestController
@@ -24,6 +28,7 @@
2428
public class StarV2Controller {
2529

2630
private final StarCommandService starCommandService;
31+
private final StarQueryService starQueryService;
2732

2833
// 북마크 추가 API(크롬 익스텐션에서 처음 북마크를 추가하는 경우)
2934
@Operation(summary = "북마크 추가", description = "크롬 익스텐션에서 처음 북마크를 추가하는 API로 AI 추천 키워드 리스를 반환한다.")
@@ -49,4 +54,12 @@ public ApiResponse<CreateStarResponseDTO> createStar(
4954

5055
return ApiResponse.onSuccessCreated(responseDTO);
5156
}
57+
58+
// 스타(카테고리 -> 키워드 -> 스타) 전체 조회 API
59+
@GetMapping("/2D")
60+
public ApiResponse<List<GetCategoryAndKeywordListDTO>> get2DStarList(@AuthUser Long userId) {
61+
List<GetCategoryAndKeywordListDTO> result = starQueryService.getCategoryAndKeywordList(userId);
62+
63+
return ApiResponse.onSuccess(result);
64+
}
5265
}

src/main/java/com/team_nebula/nebula/domain/star/converter/StarConverter.java

Lines changed: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.time.LocalDateTime;
66
import java.util.*;
7+
import java.util.stream.Collectors;
78

89
public class StarConverter {
910

@@ -23,46 +24,107 @@ public static GetStarOneResponseDTO convertToStarOneDto(GetStarOneResponseDTO da
2324
.build();
2425
}
2526

26-
2727
public static GetSearchedStarListResponseDTO convertToStarListDto(List<GetSearchedStarOneResponseDTO> queryResult) {
2828
if (queryResult == null || queryResult.isEmpty()) {
29-
return GetSearchedStarListResponseDTO.builder()
30-
.type("검색된 스타 - 링크 - 검색된 스타와 직접 연결된 스타")
31-
.totalStarCnt(0)
32-
.totalLinkCnt(0)
33-
.searchedStarListDto(Collections.emptyList())
34-
.linkListDto(Collections.emptyList())
35-
.linkedStarListDto(Collections.emptyList())
36-
.build();
29+
return emptySearchedStarListResponse();
3730
}
3831

32+
Set<GetStarOneResponseDTO> starSet = extractStars(queryResult);
33+
Set<GetStarOneResponseDTO> linkedStarSet = extractLinkedStars(queryResult);
34+
Set<GetLinkOneResponseDTO> linkSet = extractLinks(queryResult);
35+
36+
return buildSearchedStarListResponse(starSet, linkedStarSet, linkSet);
37+
}
38+
39+
private static GetSearchedStarListResponseDTO emptySearchedStarListResponse() {
40+
return GetSearchedStarListResponseDTO.builder()
41+
.type("검색된 스타 - 링크 - 검색된 스타와 직접 연결된 스타")
42+
.totalStarCnt(0)
43+
.totalLinkCnt(0)
44+
.searchedStarListDto(Collections.emptyList())
45+
.linkListDto(Collections.emptyList())
46+
.linkedStarListDto(Collections.emptyList())
47+
.build();
48+
}
49+
50+
private static Set<GetStarOneResponseDTO> extractStars(List<GetSearchedStarOneResponseDTO> queryResult) {
3951
Set<GetStarOneResponseDTO> starSet = new HashSet<>();
52+
queryResult.forEach(result -> Optional.ofNullable(result.getSearchedStar()).ifPresent(starSet::add));
53+
return starSet;
54+
}
55+
56+
private static Set<GetStarOneResponseDTO> extractLinkedStars(List<GetSearchedStarOneResponseDTO> queryResult) {
4057
Set<GetStarOneResponseDTO> linkedStarSet = new HashSet<>();
58+
queryResult.forEach(result -> Optional.ofNullable(result.getLinkedStar())
59+
.filter(linkedStar -> linkedStar.getTitle() != null)
60+
.ifPresent(linkedStarSet::add));
61+
return linkedStarSet;
62+
}
63+
64+
private static Set<GetLinkOneResponseDTO> extractLinks(List<GetSearchedStarOneResponseDTO> queryResult) {
4165
Set<GetLinkOneResponseDTO> linkSet = new HashSet<>();
66+
queryResult.forEach(result -> Optional.ofNullable(result.getLinkData())
67+
.filter(linkData -> linkData.getLinkId() != null)
68+
.ifPresent(linkSet::add));
69+
return linkSet;
70+
}
4271

43-
queryResult.forEach(result -> {
44-
if (result.getSearchedStar() != null) {
45-
starSet.add(result.getSearchedStar());
46-
}
47-
if (result.getLinkedStar() != null && result.getLinkedStar().getTitle() != null) {
48-
linkedStarSet.add(result.getLinkedStar());
49-
}
50-
if (result.getLinkData() != null && result.getLinkData().getLinkId() != null) {
51-
linkSet.add(result.getLinkData());
72+
private static GetSearchedStarListResponseDTO buildSearchedStarListResponse(
73+
Set<GetStarOneResponseDTO> starSet,
74+
Set<GetStarOneResponseDTO> linkedStarSet,
75+
Set<GetLinkOneResponseDTO> linkSet) {
76+
return GetSearchedStarListResponseDTO.builder()
77+
.type("검색된 스타 - 링크 - 검색된 스타와 직접 연결된 스타")
78+
.totalStarCnt(starSet.size())
79+
.totalLinkCnt(linkSet.size())
80+
.searchedStarListDto(new ArrayList<>(starSet))
81+
.linkListDto(new ArrayList<>(linkSet))
82+
.linkedStarListDto(new ArrayList<>(linkedStarSet))
83+
.build();
84+
}
85+
86+
public static List<GetCategoryAndKeywordListDTO> convertToNestedDto(List<GetCategoryKeywordStarRawDTO> rawList) {
87+
Map<String, Map<String, List<Get2DStarOneResponseDTO>>> resultMap = new HashMap<>();
88+
89+
for (GetCategoryKeywordStarRawDTO raw : rawList) {
90+
String category = raw.getCategoryName();
91+
String keyword = raw.getKeywordName();
92+
93+
resultMap.computeIfAbsent(category, k -> new HashMap<>());
94+
Map<String, List<Get2DStarOneResponseDTO>> keywordMap = resultMap.get(category);
95+
96+
if (keyword != null && raw.getStarId() != null) {
97+
keywordMap.computeIfAbsent(keyword, kw -> new ArrayList<>())
98+
.add(convertToStarDto(raw));
99+
} else if (keyword != null) {
100+
keywordMap.computeIfAbsent(keyword, kw -> new ArrayList<>());
52101
}
53-
});
102+
}
54103

55-
List<GetStarOneResponseDTO> starList = new ArrayList<>(starSet);
56-
List<GetStarOneResponseDTO> linkedStarList = new ArrayList<>(linkedStarSet);
57-
List<GetLinkOneResponseDTO> linkList = new ArrayList<>(linkSet);
104+
return resultMap.entrySet().stream()
105+
.map(categoryEntry -> GetCategoryAndKeywordListDTO.builder()
106+
.category(categoryEntry.getKey())
107+
.keywordList(categoryEntry.getValue().entrySet().stream()
108+
.map(keywordEntry -> GetKeywordAndStarListDTO.builder()
109+
.keyword(keywordEntry.getKey())
110+
.starList(keywordEntry.getValue())
111+
.build())
112+
.collect(Collectors.toList()))
113+
.build())
114+
.collect(Collectors.toList());
115+
}
58116

59-
return GetSearchedStarListResponseDTO.builder()
60-
.type("검색된 스타 - 링크 - 검색된 스타와 직접 연결된 스타")
61-
.totalStarCnt(starList.size())
62-
.totalLinkCnt(linkList.size())
63-
.searchedStarListDto(starList)
64-
.linkListDto(linkList)
65-
.linkedStarListDto(linkedStarList)
117+
private static Get2DStarOneResponseDTO convertToStarDto(GetCategoryKeywordStarRawDTO raw) {
118+
return Get2DStarOneResponseDTO.builder()
119+
.starId(raw.getStarId())
120+
.title(raw.getTitle())
121+
.siteUrl(raw.getSiteUrl())
122+
.thumbnailUrl(raw.getThumbnailUrl())
123+
.summaryAI(raw.getSummaryAI())
124+
.faviconUrl(raw.getFaviconUrl())
125+
.userMemo(raw.getUserMemo())
126+
.views(raw.getViews())
127+
.lastAccessedAt(raw.getLastAccessedAt())
66128
.build();
67129
}
68130

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.team_nebula.nebula.domain.star.dto.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonFormat;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.time.OffsetDateTime;
10+
import java.util.UUID;
11+
12+
@Getter
13+
@Builder
14+
@AllArgsConstructor
15+
@NoArgsConstructor
16+
public class Get2DStarOneResponseDTO {
17+
private UUID starId;
18+
private String title;
19+
private String siteUrl;
20+
private String thumbnailUrl;
21+
private String summaryAI;
22+
private String userMemo;
23+
private Integer views;
24+
private String faviconUrl;
25+
26+
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
27+
private OffsetDateTime lastAccessedAt;
28+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.team_nebula.nebula.domain.star.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
@Builder
12+
@AllArgsConstructor
13+
@NoArgsConstructor
14+
public class GetCategoryAndKeywordListDTO {
15+
private String category;
16+
private List<GetKeywordAndStarListDTO> keywordList;
17+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.team_nebula.nebula.domain.star.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.time.OffsetDateTime;
9+
import java.util.UUID;
10+
11+
@Getter
12+
@Builder
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
public class GetCategoryKeywordStarRawDTO {
16+
private String categoryName;
17+
private String keywordName;
18+
19+
private UUID starId;
20+
private String title;
21+
private String siteUrl;
22+
private String thumbnailUrl;
23+
private String faviconUrl;
24+
private String summaryAI;
25+
private String userMemo;
26+
private Integer views;
27+
private OffsetDateTime lastAccessedAt;
28+
29+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.team_nebula.nebula.domain.star.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
@Builder
12+
@AllArgsConstructor
13+
@NoArgsConstructor
14+
public class GetKeywordAndStarListDTO {
15+
private String keyword;
16+
private List<Get2DStarOneResponseDTO> starList;
17+
}

src/main/java/com/team_nebula/nebula/domain/star/dto/response/GetStarOneResponseDTO.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.team_nebula.nebula.domain.star.dto.response;
22

3+
import com.fasterxml.jackson.annotation.JsonFormat;
34
import lombok.AllArgsConstructor;
45
import lombok.Builder;
56
import lombok.Getter;
67
import lombok.NoArgsConstructor;
78

8-
import java.time.LocalDateTime;
9+
import java.time.OffsetDateTime;
910
import java.util.List;
1011
import java.util.UUID;
1112

@@ -21,8 +22,10 @@ public class GetStarOneResponseDTO {
2122
private String thumbnailUrl;
2223
private String summaryAI;
2324
private String userMemo;
24-
private int views;
25+
private Integer views;
2526
private String faviconUrl;
26-
private LocalDateTime lastAccessedAt;
27+
28+
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
29+
private OffsetDateTime lastAccessedAt;
2730
private List<String> keywordList;
2831
}

src/main/java/com/team_nebula/nebula/domain/star/entity/Star.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import org.springframework.data.neo4j.core.schema.Relationship;
1515

1616
import java.time.LocalDateTime;
17+
import java.time.OffsetDateTime;
18+
1719
import java.util.HashSet;
1820
import java.util.Set;
1921
import java.util.UUID;
@@ -46,7 +48,8 @@ public class Star extends BaseEntity {
4648
private Integer views;
4749

4850
@Property("lastAccessedAt")
49-
private LocalDateTime lastAccessedAt;
51+
private OffsetDateTime lastAccessedAt;
52+
5053

5154
@Property("html_file_url")
5255
private String htmlFileUrl;
@@ -65,7 +68,8 @@ public class Star extends BaseEntity {
6568

6669
@Builder
6770
public Star(String title, String siteUrl, String thumbnailUrl, String summaryAI, String userMemo, int views,
68-
LocalDateTime lastAccessedAt, String htmlFileUrl) {
71+
OffsetDateTime lastAccessedAt, String htmlFileUrl) {
72+
6973
this.id = UUID.randomUUID();
7074
this.title = title;
7175
this.siteUrl = siteUrl;
@@ -101,5 +105,6 @@ public void updateIsDeletedStatus(){
101105
this.isDeletedStatus = true;
102106
}
103107

104-
public void updateLastAccessedAt(){ this.lastAccessedAt = LocalDateTime.now(); }
108+
public void updateLastAccessedAt(){ this.lastAccessedAt = OffsetDateTime.now(); }
109+
105110
}

0 commit comments

Comments
 (0)