Skip to content

Commit

Permalink
Merge pull request #70 from Team-Shaka/develop2
Browse files Browse the repository at this point in the history
브랜치 관련 레거시 코드
  • Loading branch information
CYY1007 authored Jun 27, 2024
2 parents 58ebd00 + 8a5ab56 commit 0bb6e9d
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package treehouse.server.api.branch.business;

import treehouse.server.api.branch.presentation.dto.BranchResponseDTO;
import treehouse.server.global.entity.branch.Branch;
import treehouse.server.global.entity.member.Member;
import treehouse.server.global.entity.treeHouse.TreeHouse;

import java.util.List;

public class BranchMapper {

public static Branch toBranch(TreeHouse treeHouse, Member inviter, Member invitee) {
return Branch.builder()
.treeHouse(treeHouse)
.root(inviter)
.leaf(invitee)
.branchDegree(1)
.build();
}

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

public static BranchResponseDTO.NodeDTO toNodeDTO(Member member) {
return BranchResponseDTO.NodeDTO.builder()
.id(member.getId())
.profileImageUrl(member.getProfileImageUrl())
.memberName(member.getName())
.build();
}

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

public static BranchResponseDTO.getMemberBranchView toBranchView(List<BranchResponseDTO.NodeDTO> nodes, List<BranchResponseDTO.LinkDTO> links, Long rootId, Long leafId) {
return BranchResponseDTO.getMemberBranchView.builder()
.nodes(nodes)
.links(links)
.startId(rootId)
.endId(leafId)
.build();
}

public static BranchResponseDTO.getCompleteBranchView toCompleteBranchView(List<BranchResponseDTO.NodeDTO> nodes, List<BranchResponseDTO.LinkDTO> links) {
return BranchResponseDTO.getCompleteBranchView.builder()
.nodes(nodes)
.links(links)
.build();
}
}
163 changes: 163 additions & 0 deletions src/main/java/treehouse/server/api/branch/business/BranchService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package treehouse.server.api.branch.business;

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import treehouse.server.api.branch.implementation.BranchCommandAdapter;
import treehouse.server.api.branch.implementation.BranchQueryAdapter;
import treehouse.server.api.branch.presentation.dto.BranchResponseDTO;
import treehouse.server.api.member.implementation.MemberQueryAdapter;
import treehouse.server.api.treehouse.implementation.TreehouseQueryAdapter;
import treehouse.server.global.entity.User.User;
import treehouse.server.global.entity.branch.Branch;
import treehouse.server.global.entity.member.Member;
import treehouse.server.global.entity.treeHouse.TreeHouse;

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

@Service
@AllArgsConstructor
@Slf4j
public class BranchService {

private final BranchCommandAdapter branchCommandAdapter;
private final BranchQueryAdapter branchQueryAdapter;
private final TreehouseQueryAdapter treehouseQueryAdapter;
private final MemberQueryAdapter memberQueryAdapter;

@Transactional
public void createBranch(TreeHouse treeHouse, Member inviter, Member invitee) {
Branch branch = BranchMapper.toBranch(treeHouse, inviter, invitee);
branchCommandAdapter.createBranch(branch);
}

/**
* 트리하우스 내 두 멤버 사이의 가장 짧은 거리를 계산하여 브랜치 뷰를 반환합니다.
* @param user
* @param treehouseId
* @param targetMemberId
* @return BranchResponseDTO.getMemberBranchView
*/
@Transactional(readOnly = true)
public BranchResponseDTO.getMemberBranchView getMemberBranchView(User user, Long treehouseId, Long targetMemberId) {

TreeHouse treeHouse = treehouseQueryAdapter.getTreehouseById(treehouseId);
List<Branch> branches = branchQueryAdapter.findAllByTreeHouse(treeHouse); // 트리하우스 내 모든 브랜치 조회
Member member = memberQueryAdapter.findByUserAndTreehouse(user, treeHouse); // 현재 로그인한 트리하우스 멤버
Long rootId = member.getId(); // 현재 로그인한 트리하우스 멤버의 ID
BranchResponseDTO.ShortestPathResult shortestPathResult = findShortestDistance(branches, rootId, targetMemberId); // 최단 거리 계산

// 최단 거리 결과를 이용해 브랜치 뷰 생성
// Node 정보 생성
List<BranchResponseDTO.NodeDTO> nodes = shortestPathResult.getPath().stream()
.map(memberId -> memberQueryAdapter.findById(memberId))
.map(BranchMapper::toNodeDTO)
.collect(Collectors.toList());

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

// 브랜치 뷰 생성
return BranchMapper.toBranchView(nodes, links, rootId, targetMemberId);

}

@Transactional(readOnly = true)
public BranchResponseDTO.getCompleteBranchView getCompleteBranchView(User user, Long treehouseId) {
TreeHouse treeHouse = treehouseQueryAdapter.getTreehouseById(treehouseId);
List<Branch> branches = branchQueryAdapter.findAllByTreeHouse(treeHouse); // 트리하우스 내 모든 브랜치 조회
Set<Long> memberIds = new HashSet<>();
List<BranchResponseDTO.NodeDTO> nodes = new ArrayList<>();
List<BranchResponseDTO.LinkDTO> links = new ArrayList<>();

// 모든 멤버 ID를 수집
for (Branch branch : branches) {
memberIds.add(branch.getRoot().getId());
memberIds.add(branch.getLeaf().getId());
links.add(BranchMapper.toLinkDTO(branch.getRoot().getId(), branch.getLeaf().getId()));
}

// 모든 멤버 정보를 조회
for (Long memberId : memberIds) {
Member member = memberQueryAdapter.findById(memberId);
nodes.add(BranchMapper.toNodeDTO(member));
}

// 브랜치 뷰 생성
return BranchMapper.toCompleteBranchView(nodes, links);
}

/**
* 두 멤버 사이의 가장 짧은 거리를 계산하여 정수값으로 니다.
* @param treeHouse
* @param rootId // 시작 멤버 ID
* @param leafId // 끝(대상) 멤버 ID
* @return 두 멤버 사이의 가장 짧은 거리
*/

public Integer calculateBranchDegree(TreeHouse treeHouse, Long rootId, Long leafId) {
// 두 멤버 사이의 모든 Branch 엔티티를 찾습니다.
List<Branch> branches = branchQueryAdapter.findAllByTreeHouse(treeHouse);

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

return shortestDistance;
}

/**
* BFS 알고리즘을 이용해 두 멤버 사이의 가장 짧은 거리를 계산합니다.
* @param branches
* @param startMemberId
* @param endMemberId
* @return 두 멤버 사이의 가장 짧은 거리
*/

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());
}

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

while (!queue.isEmpty()) {
Long current = queue.poll();
if (current.equals(endMemberId)) {
break; // 목표 노드에 도달
}
for (Long neighbor : adjacencyList.getOrDefault(current, Collections.emptyList())) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
queue.add(neighbor);
prev.put(neighbor, current);
}
}
}

// 경로 복원 및 결과 생성
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 BranchMapper.toShortestPathResult(distance, path);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package treehouse.server.api.branch.implementation;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import treehouse.server.api.branch.persistence.BranchRepository;
import treehouse.server.global.annotations.Adapter;
import treehouse.server.global.entity.branch.Branch;

@Adapter
@RequiredArgsConstructor
@Slf4j
public class BranchCommandAdapter {

private final BranchRepository branchRepository;

public void createBranch(Branch branch) {
branchRepository.save(branch);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package treehouse.server.api.branch.implementation;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import treehouse.server.api.branch.persistence.BranchRepository;
import treehouse.server.global.annotations.Adapter;
import treehouse.server.global.entity.branch.Branch;
import treehouse.server.global.entity.treeHouse.TreeHouse;

import java.util.List;

@Adapter
@RequiredArgsConstructor
@Slf4j
public class BranchQueryAdapter {

private final BranchRepository branchRepository;

public List<Branch> findAllByTreeHouse(TreeHouse treeHouse) {
return branchRepository.findAllByTreeHouse(treeHouse);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package treehouse.server.api.branch.persistence;

import org.springframework.data.jpa.repository.JpaRepository;
import treehouse.server.global.entity.branch.Branch;
import treehouse.server.global.entity.treeHouse.TreeHouse;

import java.util.List;

public interface BranchRepository extends JpaRepository<Branch, Long>{
List<Branch> findAllByTreeHouse(TreeHouse treeHouse);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package treehouse.server.api.branch.presentation;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import treehouse.server.api.branch.business.BranchService;
import treehouse.server.api.branch.presentation.dto.BranchResponseDTO;
import treehouse.server.global.common.CommonResponse;
import treehouse.server.global.entity.User.User;
import treehouse.server.global.security.handler.annotation.AuthMember;

@RequiredArgsConstructor
@RestController
@Slf4j
@Tag(name = "🔗 Branch API", description = "트리하우스 브랜치 관련 API 입니다.")
@RequestMapping("/treehouses/{treehouseId}/branches")
public class BranchApi {

private final BranchService branchService;

@Operation(summary = "두 멤버 간 브랜치 뷰 API 🔑", description = "트리하우스 내 두 멤버 사이의 브랜치 뷰를 반환합니다.")
@GetMapping
public CommonResponse<BranchResponseDTO.getMemberBranchView> getMemberBranchView(
@PathVariable(name = "treehouseId") Long treehouseId,
@RequestParam(name = "targetMemberId") Long targetMemberId,
@AuthMember @Parameter(hidden = true) User user
)
{
return CommonResponse.onSuccess(branchService.getMemberBranchView(user, treehouseId, targetMemberId));
}

@Operation(summary = "트리하우스 전체 브랜치 뷰 API 🔑", description = "트리하우스 내 모든 멤버 사이의 브랜치 뷰를 반환합니다.")
@GetMapping("/complete")
public CommonResponse<BranchResponseDTO.getCompleteBranchView> getCompleteBranchView(
@PathVariable(name = "treehouseId") Long treehouseId,
@AuthMember @Parameter(hidden = true) User user
)
{
return CommonResponse.onSuccess(branchService.getCompleteBranchView(user, treehouseId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package treehouse.server.api.branch.presentation.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;

}

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

}


@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class getMemberBranchView {
private List<NodeDTO> nodes;
private List<LinkDTO> links;
private Long startId; // 시작 노드의 memberId
private Long endId; // 끝 노드의 memberId
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class getCompleteBranchView {
private List<NodeDTO> nodes;
private List<LinkDTO> links;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public Member getMember(User user){
}

public Member findByUserAndTreehouse(User user, TreeHouse treehouse) {
return memberRepository.findByUserAndTreeHouse(user, treehouse).orElseThrow(() -> new MemberException(GlobalErrorCode.USER_NOT_FOUND));
return memberRepository.findByUserAndTreeHouse(user, treehouse).orElseThrow(() -> new MemberException(GlobalErrorCode.MEMBER_NOT_FOUND));
}

public Member findById(Long memberId) {
return memberRepository.findById(memberId).orElseThrow(() -> new MemberException(GlobalErrorCode.MEMBER_NOT_FOUND));
}
}
Loading

0 comments on commit 0bb6e9d

Please sign in to comment.