Skip to content

Commit

Permalink
Merge pull request #56 from Team-Shaka/feature/55
Browse files Browse the repository at this point in the history
Feature/55 - 브랜치 뷰 조회 관련 기능
  • Loading branch information
koojun99 committed Mar 2, 2024
2 parents 1a8bcbf + 4060a4c commit 550bb59
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
package org.example.tree.domain.branch.controller;

import lombok.RequiredArgsConstructor;
import org.example.tree.domain.branch.dto.BranchResponseDTO;
import org.example.tree.domain.branch.service.BranchService;
import org.example.tree.global.common.ApiResponse;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class BranchController {

private final BranchService branchService;

@GetMapping("/trees/{treeId}/branchView")
public ApiResponse<BranchResponseDTO.branchView> getBranchView(
@PathVariable Long treeId,
@RequestParam("memberId") Long profileId,
@RequestHeader("Authorization") final String header
) {
String token = header.replace("Bearer ", "");
return ApiResponse.onSuccess(branchService.getBranchView(treeId, token, profileId));
}

@GetMapping("/trees/{treeId}/branchView/all")
public ApiResponse<BranchResponseDTO.branchView> getCompleteBranchView(
@PathVariable Long treeId
) {
return ApiResponse.onSuccess(branchService.getCompleteBranchView(treeId));
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package org.example.tree.domain.branch.converter;

import org.example.tree.domain.branch.dto.BranchResponseDTO;
import org.example.tree.domain.branch.entity.Branch;
import org.example.tree.domain.profile.entity.Profile;
import org.example.tree.domain.tree.entity.Tree;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class BranchConverter {

Expand All @@ -16,4 +19,35 @@ public Branch toBranch(Tree tree, Profile inviter, Profile invitee) {
.branchDegree(1)
.build();
}

public BranchResponseDTO.ShortestPathResult toShortestPathResult(int distance, List<Long> path) {
return BranchResponseDTO.ShortestPathResult.builder()
.distance(distance)
.path(path)
.build();
}

public BranchResponseDTO.NodeDTO toNodeDTO(Profile profile) {
return BranchResponseDTO.NodeDTO.builder()
.id(profile.getId())
.profileImageUrl(profile.getProfileImageUrl())
.memberName(profile.getMemberName())
.build();
}

public BranchResponseDTO.LinkDTO toLinkDTO(Long rootId, Long leafId) {
return BranchResponseDTO.LinkDTO.builder()
.sourceId(rootId)
.targetId(leafId)
.build();
}

public BranchResponseDTO.branchView toBranchView(List<BranchResponseDTO.NodeDTO> nodes, List<BranchResponseDTO.LinkDTO> links, Long rootId, Long leafId) {
return BranchResponseDTO.branchView.builder()
.nodes(nodes)
.links(links)
.startId(rootId)
.endId(leafId)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,55 @@
package org.example.tree.domain.branch.dto;

import lombok.*;

import java.util.List;

@NoArgsConstructor(access = AccessLevel.PRIVATE)

public class BranchResponseDTO {

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ShortestPathResult{
private int distance;
private List<Long> path;
}

// Node 정보를 담을 클래스
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class NodeDTO {
private Long id;
private String profileImageUrl;
private String memberName;

// 생성자, getter, setter 생략
}

// Link 정보를 담을 클래스
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class LinkDTO {
private Long sourceId;
private Long targetId;

// 생성자, getter, setter 생략
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class branchView{
private List<NodeDTO> nodes;
private List<LinkDTO> links;
private Long startId;
private Long endId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import lombok.RequiredArgsConstructor;
import org.example.tree.domain.branch.converter.BranchConverter;
import org.example.tree.domain.branch.dto.BranchResponseDTO;
import org.example.tree.domain.branch.entity.Branch;
import org.example.tree.domain.member.entity.Member;
import org.example.tree.domain.member.service.MemberQueryService;
import org.example.tree.domain.profile.entity.Profile;
import org.example.tree.domain.profile.service.ProfileQueryService;
import org.example.tree.domain.tree.entity.Tree;
Expand All @@ -11,73 +14,129 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class BranchService {
private final BranchCommandService branchCommandService;
private final BranchQueryService branchQueryService;
private final BranchConverter branchConverter;
private final ProfileQueryService profileQueryService;
private final MemberQueryService memberQueryService;
private final TreeQueryService treeQueryService;

@Transactional
public void createBranch(Tree tree,Profile inviter, Profile invitee) {
Branch branch = branchConverter.toBranch(tree, inviter, invitee);
branchCommandService.createBranch(branch);
}

@Transactional
public Integer getBranchDegree(Long treeId, Long rootId, Long leafId) {
Branch branch = branchQueryService.findBranch(treeId, rootId, leafId);
return branch.getBranchDegree();
}
// @Transactional
// public Integer getBranchDegree(Long treeId, Long rootId, Long leafId) {
// Branch branch = branchQueryService.findBranch(treeId, rootId, leafId);
// return branch.getBranchDegree();
// }

public int calculateBranchDegree(Long treeId, Long rootId, Long leafId) {
// 두 멤버 사이의 모든 Branch 엔티티를 찾습니다.
List<Branch> branches = branchQueryService.findAllBranchesInTree(treeId);

// Branch 목록을 사용하여 최단 거리를 계산합니다.
int shortestDistance = findShortestDistance(branches, rootId, leafId);
int shortestDistance = findShortestDistance(branches, rootId, leafId).getDistance();

return shortestDistance;
}

public int findShortestDistance(List<Branch> branches, Long startMemberId, Long endMemberId) {
// 멤버 ID를 기준으로 연결된 Branch를 매핑합니다.
public BranchResponseDTO.ShortestPathResult findShortestDistance(List<Branch> branches, Long startMemberId, Long endMemberId) {
Map<Long, List<Long>> adjacencyList = new HashMap<>();
Map<Long, Long> prev = new HashMap<>();
Set<Long> visited = new HashSet<>();
Queue<Long> queue = new LinkedList<>();

// 각 멤버 ID를 기준으로 연결된 Branch를 매핑
for (Branch branch : branches) {
adjacencyList.computeIfAbsent(branch.getRoot().getId(), k -> new ArrayList<>()).add(branch.getLeaf().getId());
adjacencyList.computeIfAbsent(branch.getLeaf().getId(), k -> new ArrayList<>()).add(branch.getRoot().getId()); // 양방향 그래프
adjacencyList.computeIfAbsent(branch.getLeaf().getId(), k -> new ArrayList<>()).add(branch.getRoot().getId());
}

// BFS를 위한 초기화
Queue<Long> queue = new LinkedList<>();
Map<Long, Integer> distance = new HashMap<>(); // 각 멤버까지의 거리
Set<Long> visited = new HashSet<>(); // 방문한 멤버 ID

queue.offer(startMemberId);
distance.put(startMemberId, 0);
queue.add(startMemberId);
visited.add(startMemberId);
prev.put(startMemberId, null); // 시작 노드의 선행자는 없음

// BFS 실행
while (!queue.isEmpty()) {
Long currentMemberId = queue.poll();
int currentDistance = distance.get(currentMemberId);

if (currentMemberId.equals(endMemberId)) {
return currentDistance; // 목표 멤버에 도달했다면 거리 반환
Long current = queue.poll();
if (current.equals(endMemberId)) {
break; // 목표 노드에 도달
}

// 인접한 멤버에 대해 탐색
for (Long nextMemberId : adjacencyList.getOrDefault(currentMemberId, Collections.emptyList())) {
if (!visited.contains(nextMemberId)) {
queue.offer(nextMemberId);
visited.add(nextMemberId);
distance.put(nextMemberId, currentDistance + 1);
for (Long neighbor : adjacencyList.getOrDefault(current, Collections.emptyList())) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
queue.add(neighbor);
prev.put(neighbor, current);
}
}
}

return -1; // 경로가 없는 경우
// 경로 복원 및 결과 생성
List<Long> path = new ArrayList<>();
Long current = endMemberId;
while (current != null) {
path.add(current);
current = prev.get(current);
}
Collections.reverse(path); // 경로를 역순으로 뒤집어 정상 순서로 만듦

int distance = path.size() - 1; // 거리는 경로의 길이에서 1을 뺀 값
return branchConverter.toShortestPathResult(distance, path);
}

@Transactional
public BranchResponseDTO.branchView getBranchView(Long treeId, String token, Long leafId) {
Member member = memberQueryService.findByToken(token);
Tree tree = treeQueryService.findById(treeId);
List<Branch> branches = branchQueryService.findAllBranchesInTree(treeId);
Long rootId = profileQueryService.getTreeProfile(member, tree).getId();
BranchResponseDTO.ShortestPathResult result = findShortestDistance(branches, rootId, leafId);

// Node 정보 생성
List<BranchResponseDTO.NodeDTO> nodes = result.getPath().stream()
.map(profileId -> profileQueryService.findById(profileId))
.map(branchConverter::toNodeDTO)
.collect(Collectors.toList());

// Link 정보 생성
List<BranchResponseDTO.LinkDTO> links = new ArrayList<>();
for (int i = 0; i < result.getPath().size() - 1; i++) {
links.add(branchConverter.toLinkDTO(result.getPath().get(i), result.getPath().get(i + 1)));
}

// 최종 DTO 생성 및 반환
return branchConverter.toBranchView(nodes, links, rootId, leafId);
}

@Transactional
public BranchResponseDTO.branchView getCompleteBranchView(Long treeId) {
List<Branch> branches = branchQueryService.findAllBranchesInTree(treeId);
Set<Long> allProfileIds = new HashSet<>();
List<BranchResponseDTO.NodeDTO> nodes = new ArrayList<>();
List<BranchResponseDTO.LinkDTO> links = new ArrayList<>();

// 모든 Branch로부터 모든 멤버 ID를 수집하고 LinkDTO 생성
for (Branch branch : branches) {
allProfileIds.add(branch.getRoot().getId());
allProfileIds.add(branch.getLeaf().getId());
links.add(branchConverter.toLinkDTO(branch.getRoot().getId(), branch.getLeaf().getId()));
}

// 모든 멤버 ID에 대한 Profile 정보를 조회하고 NodeDTO 생성
for (Long profileId : allProfileIds) {
Profile profile = profileQueryService.findById(profileId);
nodes.add(branchConverter.toNodeDTO(profile));
}

// 최종 BranchView DTO 생성 및 반환
return branchConverter.toBranchView(nodes, links, null, null); // startId와 endId는 전체 뷰에서는 필요 없으므로 null 처리
}
}

0 comments on commit 550bb59

Please sign in to comment.