Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

브랜치 관련 레거시 코드 #70

Merged
merged 5 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading