Enhance distance handling in routing logic: add new distance attributes, improve fallback logic, and refine Azure Maps API integration.
This commit is contained in:
parent
735d8a707b
commit
3aa86b4eea
22 changed files with 357 additions and 141 deletions
|
|
@ -116,7 +116,7 @@ export default {
|
|||
case 'warning':
|
||||
return 'secondary'
|
||||
case 'info':
|
||||
return 'primary'
|
||||
return 'secondary'
|
||||
case 'error':
|
||||
return 'exception'
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@
|
|||
|
||||
</div>
|
||||
<div class="dashboard-box-info">
|
||||
<div v-if="total !== null" class="dashboard-box-number">{{ total }}</div>
|
||||
<div v-if="completed !== null" class="dashboard-box-number">{{ completed }}</div>
|
||||
<div v-else class="dashboard-box-number"><spinner size="s"/></div>
|
||||
<div class="dashboard-box-number-text">Total</div>
|
||||
<div class="dashboard-box-number-text">Completed</div>
|
||||
</div>
|
||||
</div>
|
||||
</box>
|
||||
|
|
@ -94,8 +94,8 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapStores(useDashboardStore),
|
||||
total() {
|
||||
return this.dashboardStore.total
|
||||
completed() {
|
||||
return this.dashboardStore.completed
|
||||
},
|
||||
drafts() {
|
||||
return this.dashboardStore.drafts
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const useBulkOperationStore = defineStore('bulkOperation', {
|
|||
useNotificationStore().addNotification({
|
||||
title: 'Bulk operation',
|
||||
message: 'All your bulk operations have been completed.',
|
||||
type: 'success',
|
||||
variant: 'info',
|
||||
icon: 'stack',
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import {defineStore} from 'pinia'
|
||||
import performRequest from "@/backend.js";
|
||||
import {config} from '@/config'
|
||||
import {useNotificationStore} from "@/store/notification.js";
|
||||
|
||||
export const useDashboardStore = defineStore('dashboard', {
|
||||
state: () => ({
|
||||
|
|
@ -9,9 +10,9 @@ export const useDashboardStore = defineStore('dashboard', {
|
|||
pullTimer: null,
|
||||
}),
|
||||
getters: {
|
||||
total(state) {
|
||||
completed(state) {
|
||||
if (state.stats)
|
||||
return state.stats.total;
|
||||
return state.stats.completed;
|
||||
|
||||
return null;
|
||||
},
|
||||
|
|
@ -38,13 +39,23 @@ export const useDashboardStore = defineStore('dashboard', {
|
|||
async load() {
|
||||
const url = `${config.backendUrl}/dashboard`;
|
||||
const resp = await performRequest(this, 'GET', url, null);
|
||||
|
||||
if(this.stats?.running && this.stats.running > 0 && resp.data.running === 0) {
|
||||
useNotificationStore().addNotification({
|
||||
title: 'Calculation',
|
||||
message: 'All your calculations have been completed.',
|
||||
variant: 'info',
|
||||
icon: 'calculator',
|
||||
})
|
||||
}
|
||||
|
||||
this.stats = resp.data;
|
||||
},
|
||||
startPulling() {
|
||||
if (this.pullTimer) return
|
||||
|
||||
this.pullTimer = setTimeout(() => {
|
||||
this.pull()
|
||||
this.pullTimer = setTimeout(async () => {
|
||||
await this.pull()
|
||||
}, this.pullInterval)
|
||||
},
|
||||
stopPulling() {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ export const useNotificationStore = defineStore('notification', {
|
|||
return this.notifications.pop();
|
||||
},
|
||||
addNotification(notification) {
|
||||
|
||||
console.log("add notification", notification, this.notifications.length)
|
||||
|
||||
this.notifications.push({
|
||||
icon: notification.icon ?? null,
|
||||
message: notification.message ?? 'Unknown notification',
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
|
|||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
|
|
@ -111,7 +111,7 @@ public class SecurityConfig {
|
|||
.exceptionHandling(ex -> ex
|
||||
.defaultAuthenticationEntryPointFor(
|
||||
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
||||
new AntPathRequestMatcher("/api/**")
|
||||
PathPatternRequestMatcher.withDefaults().matcher("/api/**")
|
||||
)
|
||||
)
|
||||
.csrf(csrf -> csrf
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package de.avatic.lcc.dto.calculation.execution;
|
|||
|
||||
public class CalculationProcessingOverviewDTO {
|
||||
|
||||
private Integer total;
|
||||
private Integer completed;
|
||||
|
||||
private Integer running;
|
||||
|
||||
|
|
@ -11,12 +11,12 @@ public class CalculationProcessingOverviewDTO {
|
|||
private Integer failed;
|
||||
|
||||
|
||||
public Integer getTotal() {
|
||||
return total;
|
||||
public Integer getCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public void setTotal(Integer total) {
|
||||
this.total = total;
|
||||
public void setCompleted(Integer completed) {
|
||||
this.completed = completed;
|
||||
}
|
||||
|
||||
public Integer getRunning() {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public class Distance {
|
|||
|
||||
private DistanceMatrixState state;
|
||||
|
||||
private Integer retries;
|
||||
|
||||
private Integer fromNodeId;
|
||||
|
||||
|
|
@ -144,4 +145,12 @@ public class Distance {
|
|||
public void setToUserNodeId(Integer toUserNodeId) {
|
||||
this.toUserNodeId = toUserNodeId;
|
||||
}
|
||||
|
||||
public Integer getRetries() {
|
||||
return retries;
|
||||
}
|
||||
|
||||
public void setRetries(Integer retries) {
|
||||
this.retries = retries;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ public class Destination {
|
|||
|
||||
private Integer countryId;
|
||||
|
||||
private BigDecimal distanceD2d;
|
||||
|
||||
public Integer getLeadTimeD2d() {
|
||||
return leadTimeD2d;
|
||||
}
|
||||
|
|
@ -134,4 +136,12 @@ public class Destination {
|
|||
public void setDisposalCost(BigDecimal disposalCost) {
|
||||
this.disposalCost = disposalCost;
|
||||
}
|
||||
|
||||
public BigDecimal getDistanceD2d() {
|
||||
return distanceD2d;
|
||||
}
|
||||
|
||||
public void setDistanceD2d(BigDecimal distanceD2d) {
|
||||
this.distanceD2d = distanceD2d;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ public class RouteSection {
|
|||
|
||||
private Integer toRouteNodeId;
|
||||
|
||||
private Double distance;
|
||||
|
||||
public RateType getRateType() {
|
||||
return rateType;
|
||||
}
|
||||
|
|
@ -114,4 +116,12 @@ public class RouteSection {
|
|||
public void setToRouteNodeId(Integer toRouteNodeId) {
|
||||
this.toRouteNodeId = toRouteNodeId;
|
||||
}
|
||||
|
||||
public Double getDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
public void setDistance(Double distance) {
|
||||
this.distance = distance;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ public class DistanceMatrixRepository {
|
|||
String fromCol = isUsrFrom ? "from_user_node_id" : "from_node_id";
|
||||
String toCol = isUsrTo ? "to_user_node_id" : "to_node_id";
|
||||
|
||||
String query = "SELECT * FROM distance_matrix WHERE " + fromCol + " = ? AND " + toCol + " = ? AND state = ?";
|
||||
String query = "SELECT * FROM distance_matrix WHERE " + fromCol + " = ? AND " + toCol + " = ?";
|
||||
|
||||
var distance = jdbcTemplate.query(query, new DistanceMapper(),
|
||||
src.getId(), dest.getId(), DistanceMatrixState.VALID.name());
|
||||
src.getId(), dest.getId());
|
||||
|
||||
if (distance.isEmpty())
|
||||
return Optional.empty();
|
||||
|
|
@ -40,6 +40,12 @@ public class DistanceMatrixRepository {
|
|||
return Optional.of(distance.getFirst());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void updateRetries(Integer id) {
|
||||
String query = "UPDATE distance_matrix SET retries = retries + 1 WHERE id = ?";
|
||||
jdbcTemplate.update(query, id);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void saveDistance(Distance distance) {
|
||||
try {
|
||||
|
|
@ -119,10 +125,20 @@ public class DistanceMatrixRepository {
|
|||
public Distance mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
Distance entity = new Distance();
|
||||
|
||||
entity.setFromNodeId(rs.getInt("from_node_id"));
|
||||
entity.setToNodeId(rs.getInt("to_node_id"));
|
||||
entity.setFromNodeId(rs.getInt("from_user_node_id"));
|
||||
entity.setToNodeId(rs.getInt("to_user_node_id"));
|
||||
entity.setId(rs.getInt("id"));
|
||||
|
||||
var fromNodeId = rs.getInt("from_node_id");
|
||||
entity.setFromNodeId(rs.wasNull() ? null : fromNodeId);
|
||||
|
||||
var toNodeId = rs.getInt("to_node_id");
|
||||
entity.setToNodeId(rs.wasNull() ? null : toNodeId);
|
||||
|
||||
var fromUserNodeId = rs.getInt("from_user_node_id");
|
||||
entity.setFromUserNodeId(rs.wasNull() ? null : fromUserNodeId);
|
||||
|
||||
var toUserNodeId = rs.getInt("to_user_node_id");
|
||||
entity.setToUserNodeId(rs.wasNull() ? null : toUserNodeId);
|
||||
|
||||
entity.setDistance(rs.getBigDecimal("distance"));
|
||||
entity.setFromGeoLng(rs.getBigDecimal("from_geo_lng"));
|
||||
entity.setFromGeoLat(rs.getBigDecimal("from_geo_lat"));
|
||||
|
|
@ -131,6 +147,9 @@ public class DistanceMatrixRepository {
|
|||
entity.setState(DistanceMatrixState.valueOf(rs.getString("state")));
|
||||
entity.setUpdatedAt(rs.getTimestamp("updated_at").toLocalDateTime());
|
||||
|
||||
var retries = rs.getInt("retries");
|
||||
entity.setRetries(rs.wasNull() ? 0 : retries);
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public class DestinationRepository {
|
|||
}
|
||||
|
||||
@Transactional
|
||||
public void update(Integer id, Integer annualAmount, BigDecimal repackingCost, BigDecimal disposalCost, BigDecimal handlingCost, Boolean isD2d, BigDecimal d2dRate, BigDecimal d2dLeadTime) {
|
||||
public void update(Integer id, Integer annualAmount, BigDecimal repackingCost, BigDecimal disposalCost, BigDecimal handlingCost, Boolean isD2d, BigDecimal d2dRate, BigDecimal d2dLeadTime, BigDecimal distanceD2d) {
|
||||
if (id == null) {
|
||||
throw new InvalidArgumentException("ID cannot be null");
|
||||
}
|
||||
|
|
@ -99,6 +99,9 @@ public class DestinationRepository {
|
|||
setClauses.add("lead_time_d2d = :d2dLeadTime");
|
||||
parameters.put("d2dLeadTime", setD2d ? d2dLeadTime : null);
|
||||
|
||||
setClauses.add("distance_d2d = :distanceD2d");
|
||||
parameters.put("distanceD2d", distanceD2d);
|
||||
|
||||
|
||||
if (annualAmount != null) {
|
||||
setClauses.add("annual_amount = :annualAmount");
|
||||
|
|
@ -268,7 +271,7 @@ public class DestinationRepository {
|
|||
public Integer insert(Destination destination) {
|
||||
KeyHolder keyHolder = new GeneratedKeyHolder();
|
||||
|
||||
String query = "INSERT INTO premise_destination (annual_amount, premise_id, destination_node_id, country_id, rate_d2d, lead_time_d2d, is_d2d, repacking_cost, handling_cost, disposal_cost, geo_lat, geo_lng) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
String query = "INSERT INTO premise_destination (annual_amount, premise_id, destination_node_id, country_id, rate_d2d, lead_time_d2d, is_d2d, repacking_cost, handling_cost, disposal_cost, geo_lat, geo_lng, distance_d2d) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
jdbcTemplate.update(connection -> {
|
||||
var ps = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
|
||||
|
|
@ -297,6 +300,8 @@ public class DestinationRepository {
|
|||
ps.setBigDecimal(11, destination.getGeoLat());
|
||||
ps.setBigDecimal(12, destination.getGeoLng());
|
||||
|
||||
ps.setBigDecimal(13, destination.getDistanceD2d());
|
||||
|
||||
return ps;
|
||||
}, keyHolder);
|
||||
|
||||
|
|
@ -363,6 +368,8 @@ public class DestinationRepository {
|
|||
entity.setGeoLng(rs.getBigDecimal("geo_lng"));
|
||||
entity.setCountryId(rs.getInt("country_id"));
|
||||
|
||||
entity.setDistanceD2d(rs.getBigDecimal("distance_d2d"));
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import org.springframework.jdbc.support.GeneratedKeyHolder;
|
|||
import org.springframework.jdbc.support.KeyHolder;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
|
@ -45,8 +46,8 @@ public class RouteSectionRepository {
|
|||
}
|
||||
|
||||
public Integer insert(RouteSection premiseRouteSection) {
|
||||
String sql = "INSERT INTO premise_route_section (premise_route_id, from_route_node_id, to_route_node_id, list_position, transport_type, rate_type, is_pre_run, is_main_run, is_post_run, is_outdated) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
String sql = "INSERT INTO premise_route_section (premise_route_id, from_route_node_id, to_route_node_id, list_position, transport_type, rate_type, is_pre_run, is_main_run, is_post_run, is_outdated, distance) " +
|
||||
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
KeyHolder keyHolder = new GeneratedKeyHolder();
|
||||
|
||||
jdbcTemplate.update(connection -> {
|
||||
|
|
@ -61,6 +62,7 @@ public class RouteSectionRepository {
|
|||
ps.setBoolean(8, premiseRouteSection.getMainRun());
|
||||
ps.setBoolean(9, premiseRouteSection.getPostRun());
|
||||
ps.setBoolean(10, premiseRouteSection.getOutdated());
|
||||
ps.setBigDecimal(11, premiseRouteSection.getDistance() == null ? null : BigDecimal.valueOf(premiseRouteSection.getDistance()));
|
||||
return ps;
|
||||
}, keyHolder);
|
||||
|
||||
|
|
@ -92,6 +94,9 @@ public class RouteSectionRepository {
|
|||
entity.setPostRun(rs.getBoolean("is_post_run"));
|
||||
entity.setOutdated(rs.getBoolean("is_outdated"));
|
||||
|
||||
var distance = rs.getBigDecimal("distance");
|
||||
entity.setDistance(rs.wasNull() ? null : distance.doubleValue());
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import de.avatic.lcc.model.db.premises.route.*;
|
|||
import de.avatic.lcc.repositories.NodeRepository;
|
||||
import de.avatic.lcc.repositories.premise.*;
|
||||
import de.avatic.lcc.repositories.users.UserNodeRepository;
|
||||
import de.avatic.lcc.service.calculation.DistanceService;
|
||||
import de.avatic.lcc.service.calculation.RoutingService;
|
||||
import de.avatic.lcc.service.transformer.premise.DestinationTransformer;
|
||||
import de.avatic.lcc.service.users.AuthorizationService;
|
||||
|
|
@ -35,8 +36,9 @@ public class DestinationService {
|
|||
private final PremiseRepository premiseRepository;
|
||||
private final UserNodeRepository userNodeRepository;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final DistanceService distanceService;
|
||||
|
||||
public DestinationService(DestinationRepository destinationRepository, DestinationTransformer destinationTransformer, RouteRepository routeRepository, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, RoutingService routingService, NodeRepository nodeRepository, PremiseRepository premiseRepository, UserNodeRepository userNodeRepository, AuthorizationService authorizationService) {
|
||||
public DestinationService(DestinationRepository destinationRepository, DestinationTransformer destinationTransformer, RouteRepository routeRepository, RouteSectionRepository routeSectionRepository, RouteNodeRepository routeNodeRepository, RoutingService routingService, NodeRepository nodeRepository, PremiseRepository premiseRepository, UserNodeRepository userNodeRepository, AuthorizationService authorizationService, DistanceService distanceService) {
|
||||
this.destinationRepository = destinationRepository;
|
||||
this.destinationTransformer = destinationTransformer;
|
||||
this.routeRepository = routeRepository;
|
||||
|
|
@ -47,6 +49,7 @@ public class DestinationService {
|
|||
this.premiseRepository = premiseRepository;
|
||||
this.userNodeRepository = userNodeRepository;
|
||||
this.authorizationService = authorizationService;
|
||||
this.distanceService = distanceService;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -199,11 +202,30 @@ public class DestinationService {
|
|||
destinationUpdateDTO.getDisposalCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getDisposalCost().doubleValue()),
|
||||
destinationUpdateDTO.getHandlingCost() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getHandlingCost().doubleValue()),
|
||||
destinationUpdateDTO.getD2d(), destinationUpdateDTO.getRateD2d() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getRateD2d().doubleValue()),
|
||||
destinationUpdateDTO.getLeadtimeD2d() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getLeadtimeD2d())
|
||||
destinationUpdateDTO.getLeadtimeD2d() == null ? null : BigDecimal.valueOf(destinationUpdateDTO.getLeadtimeD2d()),
|
||||
destinationUpdateDTO.getD2d() ? getD2dDistance(id) : null
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private BigDecimal getD2dDistance(Integer destinationId) {
|
||||
var dest = destinationRepository.getById(destinationId);
|
||||
|
||||
if(dest.isPresent()) {
|
||||
var premise = premiseRepository.getPremiseById(dest.get().getPremiseId());
|
||||
|
||||
if(premise.isPresent()) {
|
||||
boolean isUserNode = premise.get().getSupplierNodeId() == null;
|
||||
|
||||
var from = isUserNode ? userNodeRepository.getById(premise.get().getUserSupplierNodeId()) : nodeRepository.getById(premise.get().getSupplierNodeId());
|
||||
var to = nodeRepository.getById(dest.get().getDestinationNodeId());
|
||||
|
||||
return BigDecimal.valueOf(distanceService.getDistanceForNode(from.orElseThrow(), to.orElseThrow()));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<RouteIds, List<RouteInformation>> findRoutes(List<Premise> premisses, Map<Integer, List<Integer>> routingRequest) {
|
||||
|
||||
Map<RouteIds, List<RouteInformation>> routes = new HashMap<>();
|
||||
|
|
@ -267,6 +289,7 @@ public class DestinationService {
|
|||
premiseRouteSection.setFromRouteNodeId(fromNodeId);
|
||||
premiseRouteSection.setToRouteNodeId(toNodeId);
|
||||
|
||||
|
||||
routeSectionRepository.insert(premiseRouteSection);
|
||||
|
||||
fromNodeId = toNodeId;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package de.avatic.lcc.service.api;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.avatic.lcc.model.azuremaps.route.RouteDirectionsResponse;
|
||||
import de.avatic.lcc.model.db.nodes.Distance;
|
||||
import de.avatic.lcc.model.db.nodes.DistanceMatrixState;
|
||||
|
|
@ -10,6 +12,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
|
|
@ -25,14 +28,18 @@ public class DistanceApiService {
|
|||
|
||||
private final DistanceMatrixRepository distanceMatrixRepository;
|
||||
private final RestTemplate restTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final GeoApiService geoApiService;
|
||||
|
||||
@Value("${azure.maps.subscription.key}")
|
||||
private String subscriptionKey;
|
||||
|
||||
public DistanceApiService(DistanceMatrixRepository distanceMatrixRepository,
|
||||
RestTemplate restTemplate) {
|
||||
RestTemplate restTemplate, ObjectMapper objectMapper, GeoApiService geoApiService) {
|
||||
this.distanceMatrixRepository = distanceMatrixRepository;
|
||||
this.restTemplate = restTemplate;
|
||||
this.objectMapper = objectMapper;
|
||||
this.geoApiService = geoApiService;
|
||||
}
|
||||
|
||||
public Optional<Integer> getDistance(Location from, Location to) {
|
||||
|
|
@ -70,22 +77,55 @@ public class DistanceApiService {
|
|||
|
||||
Optional<Distance> cachedDistance = distanceMatrixRepository.getDistance(from, isUsrFrom, to, isUsrTo);
|
||||
|
||||
if (cachedDistance.isPresent()) {
|
||||
if (cachedDistance.isPresent() && cachedDistance.get().getState() == DistanceMatrixState.VALID) {
|
||||
logger.info("Found cached distance from node {} to node {}", from.getExternalMappingId(), to.getExternalMappingId());
|
||||
return cachedDistance;
|
||||
}
|
||||
|
||||
logger.info("Fetching distance from Azure Maps for nodes {} to {}", from.getExternalMappingId(), to.getExternalMappingId());
|
||||
Optional<Distance> fetchedDistance = fetchDistanceFromAzureMaps(from, isUsrFrom, to, isUsrTo);
|
||||
if (cachedDistance.isPresent() && cachedDistance.get().getState() == DistanceMatrixState.EXCEPTION) {
|
||||
if (cachedDistance.get().getRetries() >= 3)
|
||||
return Optional.empty();
|
||||
|
||||
if (fetchedDistance.isPresent()) {
|
||||
distanceMatrixRepository.saveDistance(fetchedDistance.get());
|
||||
return fetchedDistance;
|
||||
distanceMatrixRepository.updateRetries(cachedDistance.get().getId());
|
||||
}
|
||||
|
||||
logger.info("Fetching distance from Azure Maps for nodes {} to {}", from.getExternalMappingId(), to.getExternalMappingId());
|
||||
AzureMapResponse distanceResponse = fetchDistanceFromAzureMaps(from, isUsrFrom, to, isUsrTo, true);
|
||||
|
||||
if (distanceResponse.distance != null) {
|
||||
distanceMatrixRepository.saveDistance(distanceResponse.distance);
|
||||
return Optional.of(distanceResponse.distance);
|
||||
}
|
||||
|
||||
if(distanceResponse.errorType != AzureMapsErrorType.NO_ERROR) {
|
||||
distanceMatrixRepository.saveDistance(getErrorDistance(cachedDistance, from, isUsrFrom, to, isUsrTo));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private Distance getErrorDistance(Optional<Distance> cachedDistance, Node from, boolean isUsrFrom, Node to, boolean isUsrTo) {
|
||||
var distance = cachedDistance.orElse(new Distance());
|
||||
|
||||
distance.setState(DistanceMatrixState.EXCEPTION);
|
||||
distance.setUpdatedAt(LocalDateTime.now());
|
||||
distance.setRetries(distance.getRetries() == null ? 0 : distance.getRetries() + 1);
|
||||
|
||||
if(cachedDistance.isEmpty()) {
|
||||
distance.setFromUserNodeId(isUsrFrom ? from.getId() : null);
|
||||
distance.setFromNodeId(isUsrFrom ? null : from.getId());
|
||||
distance.setToUserNodeId(isUsrTo ? to.getId() : null);
|
||||
distance.setToNodeId(isUsrTo ? null : to.getId());
|
||||
|
||||
distance.setFromGeoLat(from.getGeoLat());
|
||||
distance.setFromGeoLng(from.getGeoLng());
|
||||
distance.setToGeoLat(to.getGeoLat());
|
||||
distance.setToGeoLng(to.getGeoLng());
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
private RouteDirectionsResponse fetchDistanceFromAzureMaps(BigDecimal fromLat, BigDecimal fromLng, BigDecimal toLat, BigDecimal toLng) {
|
||||
String url = UriComponentsBuilder.fromUriString(AZURE_MAPS_ROUTE_API)
|
||||
.queryParam("api-version", "1.0")
|
||||
|
|
@ -99,48 +139,21 @@ public class DistanceApiService {
|
|||
return restTemplate.getForObject(url, RouteDirectionsResponse.class);
|
||||
}
|
||||
|
||||
//TODO;
|
||||
// private Optional<Distance> handleAzureMapsError(HttpClientErrorException e,
|
||||
// Node from, boolean isUsrFrom,
|
||||
// Node to, boolean isUsrTo) {
|
||||
// try {
|
||||
// String responseBody = e.getResponseBodyAsString();
|
||||
// JsonNode errorNode = objectMapper.readTree(responseBody);
|
||||
//
|
||||
// String errorCode = errorNode.path("error").path("code").asText();
|
||||
// String errorMessage = errorNode.path("error").path("message").asText();
|
||||
//
|
||||
// logger.warn("Azure Maps API Error for nodes {} to {}: {} - {}",
|
||||
// from.getExternalMappingId(), to.getExternalMappingId(),
|
||||
// errorCode, errorMessage);
|
||||
//
|
||||
// // Spezifische Fehlerbehandlung
|
||||
// if (errorMessage.contains("MAP_MATCHING_FAILURE") ||
|
||||
// errorMessage.contains("NO_ROUTE_FOUND")) {
|
||||
//
|
||||
// logger.info("Route calculation failed, falling back to straight line distance");
|
||||
//
|
||||
// // Fallback auf Luftlinie
|
||||
// double straightLineKm = calculateHaversineDistance(
|
||||
// from.getGeoLat(), from.getGeoLng(),
|
||||
// to.getGeoLat(), to.getGeoLng()
|
||||
// );
|
||||
//
|
||||
// return createStraightLineDistance(from, isUsrFrom, to, isUsrTo, straightLineKm);
|
||||
// }
|
||||
//
|
||||
// } catch (Exception parseException) {
|
||||
// logger.error("Failed to parse Azure Maps error response", parseException);
|
||||
// }
|
||||
//
|
||||
// return Optional.empty();
|
||||
// }
|
||||
|
||||
private Optional<Distance> fetchDistanceFromAzureMaps(Node from, boolean isUsrFrom, Node to, boolean isUsrTo) {
|
||||
private AzureMapResponse fetchDistanceFromAzureMaps(Node from, boolean isUsrFrom, Node to, boolean isUsrTo, boolean allowFixing) {
|
||||
try {
|
||||
|
||||
RouteDirectionsResponse response = fetchDistanceFromAzureMaps(from.getGeoLat(), from.getGeoLng(), to.getGeoLat(), to.getGeoLng());
|
||||
return convertToDistance(response, from, isUsrFrom, to, isUsrTo);
|
||||
} catch (Exception e) {
|
||||
if (HttpClientErrorException.class.isAssignableFrom(e.getClass()))
|
||||
return handleAzureMapsError((HttpClientErrorException) e, from, isUsrFrom, to, isUsrTo, allowFixing);
|
||||
|
||||
logger.error("Error fetching distance from Azure Maps", e);
|
||||
return new AzureMapResponse(null, AzureMapsErrorType.OTHER_ERROR, null);
|
||||
}
|
||||
}
|
||||
|
||||
private AzureMapResponse convertToDistance(RouteDirectionsResponse response, Node from, boolean isUsrFrom, Node to, boolean isUsrTo) {
|
||||
if (response != null && response.getRoutes() != null && !response.getRoutes().isEmpty()) {
|
||||
Integer distanceInMeters = response.getRoutes().getFirst().getSummary().getLengthInMeters();
|
||||
|
||||
|
|
@ -170,18 +183,100 @@ public class DistanceApiService {
|
|||
distance.setState(DistanceMatrixState.VALID);
|
||||
distance.setUpdatedAt(LocalDateTime.now());
|
||||
|
||||
// reset to 0 if on success
|
||||
distance.setRetries(0);
|
||||
|
||||
logger.info("Successfully fetched distance: {} meters", distanceInMeters);
|
||||
return Optional.of(distance);
|
||||
return new AzureMapResponse(distance, AzureMapsErrorType.NO_ERROR, null);
|
||||
} else {
|
||||
logger.warn("No routes found in Azure Maps response");
|
||||
logger.error("No routes found in Azure Maps response");
|
||||
return new AzureMapResponse(null, AzureMapsErrorType.ROUTE_ERROR, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//TODO parse 400 Bad Request on GET request for "https://atlas.microsoft.com/route/directions/json": "{<EOL> "error": {<EOL> "code": "400 BadRequest",<EOL> "message": "Engine error while executing route request: MAP_MATCHING_FAILURE: Destination (31.364, 121.598)"<EOL> }<EOL>}
|
||||
// "{<EOL> "error": {<EOL> "code": "400 BadRequest",<EOL> "message": "Engine error while executing route request: NO_ROUTE_FOUND: Origin and destination have different ProductId's."<EOL> }<EOL>}"
|
||||
logger.error("Error fetching distance from Azure Maps", e);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
private AzureMapResponse handleAzureMapsError(HttpClientErrorException e,
|
||||
Node from, boolean isUsrFrom,
|
||||
Node to, boolean isUsrTo, boolean allowFixing) {
|
||||
|
||||
try {
|
||||
String responseBody = e.getResponseBodyAsString();
|
||||
JsonNode errorNode = objectMapper.readTree(responseBody);
|
||||
|
||||
String errorCode = errorNode.path("error").path("code").asText();
|
||||
String errorMessage = errorNode.path("error").path("message").asText();
|
||||
|
||||
logger.warn("Azure Maps API Error for nodes {} ({}) to {} ({}): {} - {}",
|
||||
from.getExternalMappingId(), from.getId(), to.getExternalMappingId(), to.getId(),
|
||||
errorCode, errorMessage);
|
||||
|
||||
if (errorMessage.contains("NO_ROUTE_FOUND"))
|
||||
return new AzureMapResponse(null, AzureMapsErrorType.ROUTE_ERROR, null);
|
||||
|
||||
if (errorMessage.contains("MAP_MATCHING_FAILURE")) {
|
||||
if (errorMessage.contains("Destination")) {
|
||||
if (allowFixing) {
|
||||
var fixedNode = fixNode(to);
|
||||
|
||||
if (fixedNode != null)
|
||||
return fetchDistanceFromAzureMaps(from, isUsrFrom, fixedNode, isUsrTo, false);
|
||||
}
|
||||
|
||||
return new AzureMapResponse(null, AzureMapsErrorType.NODE_ERROR, AzureMapsDefectiveNode.FROM);
|
||||
}
|
||||
|
||||
if (errorMessage.contains("Origin")) {
|
||||
if (allowFixing) {
|
||||
var fixedNode = fixNode(from);
|
||||
|
||||
if (fixedNode != null)
|
||||
return fetchDistanceFromAzureMaps(fixedNode, isUsrFrom, to, isUsrTo, false);
|
||||
}
|
||||
|
||||
return new AzureMapResponse(null, AzureMapsErrorType.NODE_ERROR, AzureMapsDefectiveNode.TO);
|
||||
}
|
||||
|
||||
return new AzureMapResponse(null, AzureMapsErrorType.NODE_ERROR, AzureMapsDefectiveNode.UNKNOWN);
|
||||
}
|
||||
|
||||
} catch (Exception parseException) {
|
||||
logger.error("Failed to parse Azure Maps error response", parseException);
|
||||
}
|
||||
|
||||
return new AzureMapResponse(null, AzureMapsErrorType.OTHER_ERROR, null);
|
||||
}
|
||||
|
||||
private Node fixNode(Node node) {
|
||||
|
||||
logger.info("Try to fix node {} ({}) ", node.getExternalMappingId(), node.getId());
|
||||
|
||||
Location location = geoApiService.locate(node.getAddress() + ", " + node.getCountryId());
|
||||
|
||||
if (location != null && location.getLatitude() != null && location.getLongitude() != null) {
|
||||
if (0 != BigDecimal.valueOf(location.getLatitude()).compareTo(node.getGeoLat()) || 0 != BigDecimal.valueOf(location.getLongitude()).compareTo(node.getGeoLng())) {
|
||||
logger.info("Fixed node {} ({}) coordinates {}, {} -> {}, {}", node.getExternalMappingId(), node.getId(), node.getGeoLat(), node.getGeoLng(), location.getLatitude(), location.getLongitude());
|
||||
|
||||
node.setGeoLng(BigDecimal.valueOf(location.getLongitude()));
|
||||
node.setGeoLat(BigDecimal.valueOf(location.getLatitude()));
|
||||
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum AzureMapsErrorType {
|
||||
NO_ERROR, NODE_ERROR, ROUTE_ERROR, OTHER_ERROR
|
||||
}
|
||||
|
||||
private enum AzureMapsDefectiveNode {
|
||||
FROM, TO, UNKNOWN
|
||||
}
|
||||
|
||||
private record AzureMapResponse(Distance distance, AzureMapsErrorType errorType,
|
||||
AzureMapsDefectiveNode defectiveNode) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ public class GeoApiService {
|
|||
}
|
||||
|
||||
public GeocodingResult geocode(String address) {
|
||||
|
||||
if (address == null || address.trim().isEmpty()) {
|
||||
logger.warn("Address is null or empty");
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import de.avatic.lcc.model.db.country.DetourIndex;
|
|||
import de.avatic.lcc.model.db.country.IsoCode;
|
||||
import de.avatic.lcc.model.db.nodes.Location;
|
||||
import de.avatic.lcc.model.db.nodes.Node;
|
||||
import de.avatic.lcc.repositories.DistanceMatrixRepository;
|
||||
import de.avatic.lcc.repositories.country.CountryRepository;
|
||||
import de.avatic.lcc.service.api.DistanceApiService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -21,29 +20,36 @@ public class DistanceService {
|
|||
this.distanceApiService = distanceApiService;
|
||||
}
|
||||
|
||||
public double getDistance(Integer srcCountryId, Location src, Integer destCountryId, Location dest, boolean fast) {
|
||||
if (fast) return getDistanceFast(srcCountryId, src, destCountryId, dest);
|
||||
public double getDistanceForLocation(Integer srcCountryId, Location src, Integer destCountryId, Location dest, boolean fast) {
|
||||
var fastDistance = getDistanceFast(srcCountryId, src, destCountryId, dest);
|
||||
|
||||
// use fast distance if more than 3000km
|
||||
if (fast || fastDistance > 3000) return fastDistance;
|
||||
|
||||
var distance = distanceApiService.getDistance(src, dest);
|
||||
if (distance.isEmpty()) return getDistanceFast(srcCountryId, src, destCountryId, dest);
|
||||
if (distance.isEmpty()) return fastDistance;
|
||||
|
||||
return distance.map(value -> value/1000).orElse(0);
|
||||
}
|
||||
|
||||
public double getDistance(Node src, Node dest, boolean fast) {
|
||||
public double getDistanceForNode(Node src, Node dest) {
|
||||
return getDistanceForNode(src, dest, false);
|
||||
}
|
||||
|
||||
public double getDistanceForNode(Node src, Node dest, boolean fast) {
|
||||
var fastDistance = getDistanceFast(src, dest);
|
||||
|
||||
// use fast distance if more than 3000km
|
||||
if (fast || fastDistance > 3000) return fastDistance;
|
||||
|
||||
var distance = distanceApiService.getDistance(src, src.isUserNode(), dest, dest.isUserNode());
|
||||
if (distance.isEmpty()) return getDistanceFast(src, dest);
|
||||
if (distance.isEmpty()) return fastDistance;
|
||||
|
||||
return distance.map(value -> value.getDistance().intValue()/1000).orElse(0);
|
||||
}
|
||||
|
||||
|
||||
public double getDistanceFast(Integer srcCountryId, Location src, Integer destCountryId, Location dest ) {
|
||||
private double getDistanceFast(Integer srcCountryId, Location src, Integer destCountryId, Location dest ) {
|
||||
double srcLatitudeRadians = Math.toRadians(src.getLatitude());
|
||||
double srcLongitudeRad = Math.toRadians(src.getLongitude());
|
||||
double destLatitudeRadians = Math.toRadians(dest.getLatitude());
|
||||
|
|
|
|||
|
|
@ -219,6 +219,8 @@ public class RoutingService {
|
|||
routeSection.setPostRun(section.getType().equals(TemporaryRateObject.TemporaryRateObjectType.POST_RUN));
|
||||
routeSection.setPreRun(false);
|
||||
|
||||
routeSection.setDistance(section.getApproxDistance());
|
||||
|
||||
return routeSection;
|
||||
|
||||
}
|
||||
|
|
@ -334,7 +336,7 @@ public class RoutingService {
|
|||
}
|
||||
|
||||
finalSection.setRate(matrixRate);
|
||||
finalSection.setApproxDistance(distanceService.getDistance(container.getSourceNode(), toNode, true));
|
||||
finalSection.setApproxDistance(distanceService.getDistanceForNode(container.getSourceNode(), toNode));
|
||||
rates.add(finalSection);
|
||||
}
|
||||
|
||||
|
|
@ -699,7 +701,7 @@ public class RoutingService {
|
|||
|
||||
if (matrixRate.isPresent()) {
|
||||
matrixRateObj.setRate(matrixRate.get());
|
||||
matrixRateObj.setApproxDistance(distanceService.getDistance(startNode, endNode, true));
|
||||
matrixRateObj.setApproxDistance(distanceService.getDistanceForNode(startNode, endNode));
|
||||
container.getRates().add(matrixRateObj);
|
||||
return matrixRateObj;
|
||||
} else {
|
||||
|
|
@ -927,16 +929,16 @@ public class RoutingService {
|
|||
|
||||
sb.append(sections.getLast().getFromNode().getDebugText());
|
||||
for (var section : sections.reversed()) {
|
||||
sb.append(" -");
|
||||
sb.append(" --[");
|
||||
sb.append(section.getType().getDebugText());
|
||||
sb.append("-> ");
|
||||
sb.append("]--> ");
|
||||
sb.append(section.getToNode().getDebugText());
|
||||
}
|
||||
|
||||
} else sb.append("Empty");
|
||||
|
||||
|
||||
return String.format("Route: [%s, %s]", quality.name().charAt(0) + (isFastest ? "\uD83D\uDE80" : "") + (isCheapest ? "\uD83D\uDCB2" : ""), sb);
|
||||
return String.format("Route: [%s, %s]", quality.name() + (isFastest ? " FAST" : "") + (isCheapest ? " CHEAP" : ""), sb);
|
||||
}
|
||||
|
||||
public void setQuality(ChainQuality quality) {
|
||||
|
|
@ -1074,8 +1076,8 @@ public class RoutingService {
|
|||
}
|
||||
|
||||
private enum TemporaryRateObjectType {
|
||||
NEAR_BY("\uD83D\uDCCD"), MATRIX("\uD83D\uDCCA"), CONTAINER("\uD83D\uDCE6"), POST_RUN("\uD83D\uDE9B"), MAIN_RUN("\uD83D\uDEA2");
|
||||
|
||||
// NEAR_BY("\uD83D\uDCCD"), MATRIX("\uD83D\uDCCA"), CONTAINER("\uD83D\uDCE6"), POST_RUN("\uD83D\uDE9B"), MAIN_RUN("\uD83D\uDEA2");
|
||||
NEAR_BY("NEAR"), MATRIX("KM"), CONTAINER("CON"), POST_RUN("POST"), MAIN_RUN("MAIN");
|
||||
private final String debugText;
|
||||
|
||||
TemporaryRateObjectType(String debugText) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package de.avatic.lcc.service.calculation.execution;
|
|||
|
||||
import de.avatic.lcc.dto.calculation.execution.*;
|
||||
import de.avatic.lcc.model.db.calculations.CalculationJob;
|
||||
import de.avatic.lcc.model.db.calculations.CalculationJobPriority;
|
||||
import de.avatic.lcc.model.db.calculations.CalculationJobState;
|
||||
import de.avatic.lcc.model.db.premises.PremiseState;
|
||||
import de.avatic.lcc.model.db.properties.PropertySet;
|
||||
|
|
@ -126,7 +125,7 @@ public class CalculationJobProcessorManagementService {
|
|||
dto.setFailed(failed);
|
||||
dto.setDrafts(draft);
|
||||
dto.setRunning(running);
|
||||
dto.setTotal(completed + draft);
|
||||
dto.setCompleted(completed);
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package de.avatic.lcc.service.calculation.execution.steps;
|
||||
|
||||
import de.avatic.lcc.model.calculation.ContainerCalculationResult;
|
||||
import de.avatic.lcc.dto.generic.ContainerType;
|
||||
import de.avatic.lcc.dto.generic.RateType;
|
||||
import de.avatic.lcc.dto.generic.TransportType;
|
||||
import de.avatic.lcc.model.calculation.ContainerCalculationResult;
|
||||
import de.avatic.lcc.model.db.calculations.CalculationJobRouteSection;
|
||||
import de.avatic.lcc.model.db.nodes.Location;
|
||||
import de.avatic.lcc.model.db.nodes.Node;
|
||||
|
|
@ -70,7 +70,7 @@ public class RouteSectionCostCalculationService {
|
|||
Node fromNode = premise.getSupplierNodeId() != null ? nodeRepository.getById(premise.getSupplierNodeId()).orElseThrow() : userNodeRepository.getById(premise.getUserSupplierNodeId()).orElseThrow();
|
||||
Node toNode = nodeRepository.getById(destination.getDestinationNodeId()).orElseThrow();
|
||||
|
||||
double distance = distanceService.getDistance(fromNode, toNode, false);
|
||||
double distance = destination.getDistanceD2d() == null ? distanceService.getDistanceForNode(fromNode, toNode) : destination.getDistanceD2d().doubleValue();
|
||||
result.setDistance(BigDecimal.valueOf(distance));
|
||||
|
||||
// Get rate and transit time
|
||||
|
|
@ -130,8 +130,14 @@ public class RouteSectionCostCalculationService {
|
|||
// Get nodes and distance
|
||||
RouteNode fromNode = routeNodeRepository.getById(section.getFromRouteNodeId()).orElseThrow();
|
||||
RouteNode toNode = routeNodeRepository.getById(section.getToRouteNodeId()).orElseThrow();
|
||||
|
||||
if (section.getDistance() == null) {
|
||||
double distance = getDistance(fromNode, toNode);
|
||||
result.setDistance(BigDecimal.valueOf(distance));
|
||||
} else {
|
||||
result.setDistance(BigDecimal.valueOf(section.getDistance()));
|
||||
}
|
||||
|
||||
|
||||
// Get rate and transit time
|
||||
BigDecimal rate;
|
||||
|
|
@ -143,7 +149,7 @@ public class RouteSectionCostCalculationService {
|
|||
transitTime = containerRate.getLeadTime();
|
||||
} else if (RateType.MATRIX == section.getRateType()) {
|
||||
MatrixRate matrixRate = findMatrixRate(fromNode, toNode, periodId);
|
||||
rate = matrixRate.getRate().multiply(BigDecimal.valueOf(distance));
|
||||
rate = matrixRate.getRate().multiply(result.getDistance());
|
||||
transitTime = 3; // Default transit time for matrix rate
|
||||
} else if (RateType.NEAR_BY == section.getRateType()) {
|
||||
rate = BigDecimal.ZERO;
|
||||
|
|
@ -184,7 +190,7 @@ public class RouteSectionCostCalculationService {
|
|||
|
||||
BigDecimal annualCost = (containerCalculation.isWeightExceeded() ? prices.weightPrice.multiply(annualWeight) : prices.volumePrice.multiply(annualVolume));
|
||||
BigDecimal annualRiskCost = useRiskChange ? annualCost.multiply(chanceRiskFactors.getRiskFactor()) : annualCost;
|
||||
BigDecimal annualChanceCost = useRiskChange ? annualCost.multiply(chanceRiskFactors.getChanceFactor()): annualCost;
|
||||
BigDecimal annualChanceCost = useRiskChange ? annualCost.multiply(chanceRiskFactors.getChanceFactor()) : annualCost;
|
||||
|
||||
result.setAnnualRiskCost(annualRiskCost);
|
||||
result.setAnnualChanceCost(annualChanceCost);
|
||||
|
|
@ -278,8 +284,8 @@ public class RouteSectionCostCalculationService {
|
|||
|
||||
private double getDistance(RouteNode fromNode, RouteNode toNode) {
|
||||
|
||||
if(fromNode.getOutdated() || toNode.getOutdated()) {
|
||||
return distanceService.getDistance(
|
||||
if (fromNode.getOutdated() || toNode.getOutdated()) {
|
||||
return distanceService.getDistanceForLocation(
|
||||
fromNode.getCountryId(), new Location(fromNode.getGeoLng().doubleValue(), fromNode.getGeoLat().doubleValue()),
|
||||
toNode.getCountryId(), new Location(toNode.getGeoLng().doubleValue(), toNode.getGeoLat().doubleValue()), false);
|
||||
}
|
||||
|
|
@ -287,14 +293,14 @@ public class RouteSectionCostCalculationService {
|
|||
var optSrcNode = fromNode.getNodeId() == null ? userNodeRepository.getById(fromNode.getUserNodeId()) : nodeRepository.getById(fromNode.getNodeId());
|
||||
var optDestNode = toNode.getNodeId() == null ? userNodeRepository.getById(toNode.getUserNodeId()) : nodeRepository.getById(toNode.getNodeId());
|
||||
|
||||
if(optSrcNode.isEmpty() ) {
|
||||
if (optSrcNode.isEmpty()) {
|
||||
throw new NoSuchElementException("Source node not found for route section " + fromNode.getName());
|
||||
}
|
||||
if(optDestNode.isEmpty() ) {
|
||||
if (optDestNode.isEmpty()) {
|
||||
throw new NoSuchElementException("Destination node not found for route section" + toNode.getName());
|
||||
}
|
||||
|
||||
return distanceService.getDistance(optSrcNode.get(), optDestNode.get(), false);
|
||||
return distanceService.getDistanceForNode(optSrcNode.get(), optDestNode.get(), false);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,5 +7,13 @@ ALTER TABLE calculation_job
|
|||
ADD INDEX idx_priority (priority);
|
||||
|
||||
ALTER TABLE distance_matrix
|
||||
ADD COLUMN retries INT NOT NULL DEFAULT 0,
|
||||
DROP CONSTRAINT chk_distance_matrix_state,
|
||||
ADD CONSTRAINT chk_distance_matrix_state CHECK (`state` IN ('VALID', 'STALE', 'EXCEPTION'));
|
||||
|
||||
|
||||
ALTER TABLE premise_destination
|
||||
ADD COLUMN distance_d2d DECIMAL(15, 2) DEFAULT NULL COMMENT 'travel distance between the two nodes in meters';
|
||||
|
||||
ALTER TABLE premise_route_section
|
||||
ADD COLUMN distance DECIMAL(15, 2) DEFAULT NULL COMMENT 'travel distance between the two nodes in meters';
|
||||
|
|
@ -433,6 +433,7 @@ CREATE TABLE IF NOT EXISTS premise_destination
|
|||
is_d2d BOOLEAN DEFAULT FALSE,
|
||||
rate_d2d DECIMAL(15, 2) DEFAULT NULL CHECK (rate_d2d >= 0),
|
||||
lead_time_d2d INT UNSIGNED DEFAULT NULL CHECK (lead_time_d2d >= 0),
|
||||
distance_d2d DECIMAL(15, 2) DEFAULT NULL,
|
||||
repacking_cost DECIMAL(15, 2) DEFAULT NULL CHECK (repacking_cost >= 0),
|
||||
handling_cost DECIMAL(15, 2) DEFAULT NULL CHECK (handling_cost >= 0),
|
||||
disposal_cost DECIMAL(15, 2) DEFAULT NULL CHECK (disposal_cost >= 0),
|
||||
|
|
@ -496,6 +497,7 @@ CREATE TABLE IF NOT EXISTS premise_route_section
|
|||
is_main_run BOOLEAN DEFAULT FALSE,
|
||||
is_post_run BOOLEAN DEFAULT FALSE,
|
||||
is_outdated BOOLEAN DEFAULT FALSE,
|
||||
distance DECIMAL(15, 2) DEFAULT NULL COMMENT 'travel distance between the two nodes in meters',
|
||||
CONSTRAINT fk_premise_route_section_premise_route_id FOREIGN KEY (premise_route_id) REFERENCES premise_route (id),
|
||||
FOREIGN KEY (from_route_node_id) REFERENCES premise_route_node (id),
|
||||
FOREIGN KEY (to_route_node_id) REFERENCES premise_route_node (id),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue