优化
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
package com.baiyue.firstleg.dashboard;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public final class DashboardMetricsDtos {
|
||||
private DashboardMetricsDtos() {}
|
||||
|
||||
public record OperationalMetrics(
|
||||
long inTransitContainerCount,
|
||||
BigDecimal inTransitTeu,
|
||||
long unknownContainerTypeCount,
|
||||
long pendingCustomsOrderCount,
|
||||
LocalDateTime asOf
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.baiyue.firstleg.dashboard;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Mapper
|
||||
public interface DashboardMetricsMapper {
|
||||
@Select("""
|
||||
SELECT transit.container_count inTransitContainerCount,
|
||||
transit.total_teu inTransitTeu,
|
||||
transit.unknown_container_type_count unknownContainerTypeCount,
|
||||
customs.pending_customs_order_count pendingCustomsOrderCount,
|
||||
CURRENT_TIMESTAMP(3) asOf
|
||||
FROM (
|
||||
SELECT COUNT(*) container_count,
|
||||
COALESCE(SUM(container.teu), 0) total_teu,
|
||||
COALESCE(SUM(CASE WHEN container.teu IS NULL THEN 1 ELSE 0 END), 0) unknown_container_type_count
|
||||
FROM (
|
||||
SELECT DISTINCT booking.id,
|
||||
CASE
|
||||
WHEN UPPER(TRIM(booking.container_type)) LIKE '20%' THEN 1.0
|
||||
WHEN UPPER(TRIM(booking.container_type)) LIKE '40%' THEN 2.0
|
||||
WHEN UPPER(TRIM(booking.container_type)) LIKE '45%' THEN 2.25
|
||||
ELSE NULL
|
||||
END teu
|
||||
FROM fl_container_booking booking
|
||||
JOIN fl_ocean_booking ocean ON ocean.id = booking.ocean_booking_id
|
||||
WHERE booking.actual_sailing_at IS NOT NULL
|
||||
AND booking.actual_warehouse_arrival_at IS NULL
|
||||
AND booking.record_status = 'ACTIVE'
|
||||
AND ocean.status <> 'CANCELLED'
|
||||
) container
|
||||
) transit
|
||||
CROSS JOIN (
|
||||
SELECT COUNT(DISTINCT inbound.order_id) pending_customs_order_count
|
||||
FROM fl_customs_container customs_container
|
||||
JOIN fl_loading_plan_item item
|
||||
ON item.loading_plan_id = customs_container.loading_plan_id AND item.deleted = 0
|
||||
JOIN fl_inbound_order inbound
|
||||
ON inbound.id = item.inbound_id AND inbound.tenant_id = item.account_id AND inbound.deleted = 0
|
||||
WHERE customs_container.status <> 'COMPLETED'
|
||||
) customs
|
||||
""")
|
||||
MetricsProjection read();
|
||||
|
||||
record MetricsProjection(Long inTransitContainerCount, BigDecimal inTransitTeu,
|
||||
Long unknownContainerTypeCount, Long pendingCustomsOrderCount,
|
||||
LocalDateTime asOf) {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.baiyue.firstleg.dashboard;
|
||||
|
||||
import com.baiyue.firstleg.dashboard.DashboardMetricsDtos.OperationalMetrics;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Service
|
||||
public class DashboardMetricsService {
|
||||
@Resource private DashboardMetricsMapper mapper;
|
||||
|
||||
public OperationalMetrics read() {
|
||||
var row = mapper.read();
|
||||
if (row == null) return new OperationalMetrics(0, BigDecimal.ZERO, 0, 0, null);
|
||||
return new OperationalMetrics(value(row.inTransitContainerCount()),
|
||||
row.inTransitTeu() == null ? BigDecimal.ZERO : row.inTransitTeu(),
|
||||
value(row.unknownContainerTypeCount()), value(row.pendingCustomsOrderCount()), row.asOf());
|
||||
}
|
||||
|
||||
private static long value(Long value) {
|
||||
return value == null ? 0 : value;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.baiyue.firstleg.dashboard;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.dashboard.DashboardMetricsDtos.OperationalMetrics;
|
||||
import com.baiyue.firstleg.service.FirstlegScopeGuard;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/platform/v1/first-leg/dashboard")
|
||||
public class PlatformDashboardMetricsController {
|
||||
@Resource private DashboardMetricsService service;
|
||||
|
||||
@GetMapping("/operational-metrics")
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:container:list') and hasAuthority('platform:firstleg:customs:list')")
|
||||
public ApiResponse<OperationalMetrics> metrics(@AuthenticationPrincipal AuthenticatedUser user) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.read());
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.baiyue.firstleg.dashboard;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class DashboardMetricsContractTest {
|
||||
@Test void exposesThePlatformDashboardEndpointWithBothReadPermissions() throws Exception {
|
||||
assertThat(PlatformDashboardMetricsController.class.getAnnotation(RequestMapping.class).value())
|
||||
.containsExactly("/api/platform/v1/first-leg/dashboard");
|
||||
var method = PlatformDashboardMetricsController.class.getDeclaredMethod(
|
||||
"metrics", com.baiyue.common.security.AuthenticatedUser.class);
|
||||
assertThat(method.getAnnotation(GetMapping.class).value()).containsExactly("/operational-metrics");
|
||||
assertThat(method.getAnnotation(PreAuthorize.class).value())
|
||||
.contains("platform:firstleg:container:list", "platform:firstleg:customs:list");
|
||||
}
|
||||
|
||||
@Test void queryCountsPhysicalTransitContainersAndDistinctPendingOrders() {
|
||||
var sql = Arrays.stream(DashboardMetricsMapper.class.getDeclaredMethods())
|
||||
.filter(method -> method.getName().equals("read"))
|
||||
.findFirst().orElseThrow().getAnnotation(org.apache.ibatis.annotations.Select.class).value();
|
||||
var statement = String.join("\n", sql);
|
||||
assertThat(statement).contains("actual_sailing_at IS NOT NULL", "actual_warehouse_arrival_at IS NULL",
|
||||
"booking.record_status = 'ACTIVE'", "ocean.status <> 'CANCELLED'",
|
||||
"COUNT(DISTINCT inbound.order_id)", "customs_container.status <> 'COMPLETED'",
|
||||
"item.deleted = 0", "inbound.deleted = 0", "LIKE '20%'", "LIKE '40%'", "LIKE '45%'");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.baiyue.firstleg.dashboard;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DashboardMetricsServiceTest {
|
||||
@Mock DashboardMetricsMapper mapper;
|
||||
@InjectMocks DashboardMetricsService service;
|
||||
|
||||
@Test void preservesOperationalCountsAndDecimalTeu() {
|
||||
var asOf = LocalDateTime.of(2026, 9, 11, 12, 0);
|
||||
when(mapper.read()).thenReturn(new DashboardMetricsMapper.MetricsProjection(
|
||||
3L, new BigDecimal("4.25"), 1L, 7L, asOf));
|
||||
|
||||
var result = service.read();
|
||||
|
||||
assertThat(result.inTransitContainerCount()).isEqualTo(3);
|
||||
assertThat(result.inTransitTeu()).isEqualByComparingTo("4.25");
|
||||
assertThat(result.unknownContainerTypeCount()).isEqualTo(1);
|
||||
assertThat(result.pendingCustomsOrderCount()).isEqualTo(7);
|
||||
assertThat(result.asOf()).isEqualTo(asOf);
|
||||
}
|
||||
|
||||
@Test void returnsZerosWhenTheDatabaseProjectionIsAbsent() {
|
||||
when(mapper.read()).thenReturn(null);
|
||||
|
||||
var result = service.read();
|
||||
|
||||
assertThat(result.inTransitContainerCount()).isZero();
|
||||
assertThat(result.inTransitTeu()).isEqualByComparingTo("0");
|
||||
assertThat(result.pendingCustomsOrderCount()).isZero();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user