Skip to content

Commit

Permalink
[UNI-327] refactor: 로컬 캐시 테스트
Browse files Browse the repository at this point in the history
  • Loading branch information
mikekks committed Feb 19, 2025
1 parent 88cc318 commit 3e1383f
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.softeer5.uniro_backend.map.cache;

import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.softeer5.uniro_backend.map.entity.Node;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
class LightNode implements Serializable {
private long id;
private double lat;
private double lng;

@JsonProperty("core")
private boolean isCore;

public LightNode(Node node) {
this.id = node.getId();
this.lat = node.getY();
this.lng = node.getX();
this.isCore = node.isCore();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.softeer5.uniro_backend.map.cache;

import java.io.Serializable;

import com.softeer5.uniro_backend.map.entity.Route;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class LightRoute implements Serializable {
private long id;
private double distance;
private LightNode node1;
private LightNode node2;

public LightRoute(Route route) {
this.id = route.getId();
this.distance = route.getDistance();
this.node1 = new LightNode(route.getNode1());
this.node2 = new LightNode(route.getNode2());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package com.softeer5.uniro_backend.map.cache;

import static com.softeer5.uniro_backend.common.constant.UniroConst.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import org.locationtech.jts.geom.GeometryFactory;
import org.springframework.stereotype.Component;

import com.softeer5.uniro_backend.common.utils.GeoUtils;
import com.softeer5.uniro_backend.map.dto.response.AllRoutesInfo;
import com.softeer5.uniro_backend.map.dto.response.BuildingRouteResDTO;
import com.softeer5.uniro_backend.map.dto.response.CoreRouteResDTO;
import com.softeer5.uniro_backend.map.dto.response.NodeInfoResDTO;
import com.softeer5.uniro_backend.map.dto.response.RouteCoordinatesInfoResDTO;

@Component
public class RouteCacheCalculator {

private final GeometryFactory geometryFactory = GeoUtils.getInstance();

public AllRoutesInfo assembleRoutes(List<LightRoute> routes) {
Map<Long, List<LightRoute>> adjMap = new HashMap<>();
Map<Long, LightNode> nodeMap = new HashMap<>();
List<BuildingRouteResDTO> buildingRoutes = new ArrayList<>();

for (LightRoute route : routes) {

if (isBuildingRoute(route)) {
List<RouteCoordinatesInfoResDTO> routeCoordinates = new ArrayList<>();
routeCoordinates.add(RouteCoordinatesInfoResDTO.of(route.getId(), route.getNode1().getId(), route.getNode2().getId()));
buildingRoutes.add(BuildingRouteResDTO.of(route.getNode1().getId(), route.getNode2().getId(), routeCoordinates));
continue;
}

nodeMap.put(route.getNode1().getId(), route.getNode1());
nodeMap.put(route.getNode2().getId(), route.getNode2());

adjMap.computeIfAbsent(route.getNode1().getId(), k -> new ArrayList<>()).add(route);
adjMap.computeIfAbsent(route.getNode2().getId(), k -> new ArrayList<>()).add(route);

}

List<NodeInfoResDTO> nodeInfos = nodeMap.entrySet().stream()
.map(entry -> NodeInfoResDTO.of(entry.getKey(), entry.getValue().getLng(), entry.getValue().getLat()))
.toList();

List<LightNode>startNode = determineStartNodes(adjMap, nodeMap);

return AllRoutesInfo.of(nodeInfos, getCoreRoutes(adjMap, startNode), buildingRoutes);
}

private boolean isBuildingRoute(LightRoute route){
return route.getDistance() > BUILDING_ROUTE_DISTANCE - 1;
}

private List<LightNode> determineStartNodes(Map<Long, List<LightRoute>> adjMap,
Map<Long, LightNode> nodeMap) {
return nodeMap.values().stream()
.filter(node -> node.isCore()
|| (adjMap.containsKey(node.getId()) && adjMap.get(node.getId()).size() == 1))
.toList();
}

private List<CoreRouteResDTO> getCoreRoutes(Map<Long, List<LightRoute>> adjMap, List<LightNode> startNode) {
List<CoreRouteResDTO> result = new ArrayList<>();
// core node간의 BFS 할 때 방문여부를 체크하는 set
Set<Long> visitedCoreNodes = new HashSet<>();
// 길 중복을 처리하기 위한 set
Set<Long> routeSet = new HashSet<>();

// BFS 전처리
Queue<LightNode> nodeQueue = new LinkedList<>();
startNode.forEach(n-> {
nodeQueue.add(n);
visitedCoreNodes.add(n.getId());
});

// BFS
while(!nodeQueue.isEmpty()) {
// 현재 노드 (코어노드)
LightNode now = nodeQueue.poll();
for(LightRoute r : adjMap.get(now.getId())) {
// 만약 now-nxt를 연결하는 길이 이미 등록되어있다면, 해당 coreRoute는 이미 등록된 것이므로 continue;
if(routeSet.contains(r.getId())) continue;

// 다음 노드 (서브노드일수도 있고 코어노드일 수도 있음)
LightNode currentNode = now.getId() == r.getNode1().getId() ? r.getNode2() : r.getNode1();

// 코어루트를 이루는 node들을 List로 저장
List<RouteCoordinatesInfoResDTO> coreRoute = new ArrayList<>();
coreRoute.add(RouteCoordinatesInfoResDTO.of(r.getId(),now.getId(), currentNode.getId()));
routeSet.add(r.getId());

while (true) {
//코어노드를 만나면 queue에 넣을지 판단한 뒤 종료 (제자리로 돌아오는 경우도 포함)
if (currentNode.isCore() || currentNode.getId() == now.getId()) {
if (!visitedCoreNodes.contains(currentNode.getId())) {
visitedCoreNodes.add(currentNode.getId());
nodeQueue.add(currentNode);
}
break;
}
// 끝점인 경우 종료
if (adjMap.get(currentNode.getId()).size() == 1) break;

// 서브노드에 연결된 두 route 중 방문하지 않았던 route를 선택한 뒤, currentNode를 업데이트
for (LightRoute R : adjMap.get(currentNode.getId())) {
if (routeSet.contains(R.getId())) continue;
LightNode nextNode = R.getNode1().getId() == currentNode.getId() ? R.getNode2() : R.getNode1();
coreRoute.add(RouteCoordinatesInfoResDTO.of(R.getId(), currentNode.getId(), nextNode.getId()));
routeSet.add(R.getId());
currentNode = nextNode;
}
}
result.add(CoreRouteResDTO.of(now.getId(), currentNode.getId(), coreRoute));
}

}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.softeer5.uniro_backend.common.exception.custom.RouteCalculationException;
import com.softeer5.uniro_backend.common.exception.custom.RouteException;
import com.softeer5.uniro_backend.external.MapClient;
import com.softeer5.uniro_backend.map.cache.LightRoute;
import com.softeer5.uniro_backend.map.cache.RouteCacheCalculator;
import com.softeer5.uniro_backend.map.dto.request.CreateRoutesReqDTO;
import com.softeer5.uniro_backend.map.entity.Node;

Expand Down Expand Up @@ -46,11 +48,21 @@ public class MapService {
private final RevInfoRepository revInfoRepository;

private final RouteCalculator routeCalculator;
private final RouteCacheCalculator routeCacheCalculator;

private final MapClient mapClient;

private final Map<Long, List<LightRoute>> cache = new HashMap<>();

public GetAllRoutesResDTO getAllRoutes(Long univId) {
List<Route> routes = routeRepository.findAllRouteByUnivIdWithNodes(univId);

if(!cache.containsKey(univId)){
List<Route> routes = routeRepository.findAllRouteByUnivIdWithNodes(univId);
List<LightRoute> lightRoutes = routes.stream().map(LightRoute::new).toList();
cache.put(univId, lightRoutes);
}

List<LightRoute> routes = cache.get(univId);

// 맵이 존재하지 않을 경우 예외
if(routes.isEmpty()) {
Expand All @@ -60,7 +72,7 @@ public GetAllRoutesResDTO getAllRoutes(Long univId) {
RevInfo revInfo = revInfoRepository.findFirstByUnivIdOrderByRevDesc(univId)
.orElseThrow(() -> new RouteException("Revision not found", RECENT_REVISION_NOT_FOUND));

AllRoutesInfo allRoutesInfo = routeCalculator.assembleRoutes(routes);
AllRoutesInfo allRoutesInfo = routeCacheCalculator.assembleRoutes(routes);

return GetAllRoutesResDTO.of(allRoutesInfo.getNodeInfos(), allRoutesInfo.getCoreRoutes(),
allRoutesInfo.getBuildingRoutes(), revInfo.getRev());
Expand Down

0 comments on commit 3e1383f

Please sign in to comment.