发布当前测试版本至临时灰度环境 #19
+5
-6
@@ -66,7 +66,6 @@ public class PlatformCarrierScheduleQueryController {
|
||||
public ApiResponse<PageResult<QueryView>> page(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@Valid Query query) {
|
||||
FirstlegScopeGuard.platform(user);
|
||||
if (query.customerId() != null) FirstlegScopeGuard.platform(user, query.customerId());
|
||||
return ApiResponse.success(service.page(query));
|
||||
}
|
||||
|
||||
@@ -74,7 +73,7 @@ public class PlatformCarrierScheduleQueryController {
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule-query:list')")
|
||||
public ApiResponse<Detail> detail(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@PathVariable @Positive long queryId) {
|
||||
FirstlegScopeGuard.platform(user, service.customerId(queryId));
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.detail(queryId));
|
||||
}
|
||||
|
||||
@@ -82,7 +81,7 @@ public class PlatformCarrierScheduleQueryController {
|
||||
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule-query:list')")
|
||||
public ApiResponse<List<History>> history(
|
||||
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable @Positive long queryId) {
|
||||
FirstlegScopeGuard.platform(user, service.customerId(queryId));
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.history(queryId));
|
||||
}
|
||||
|
||||
@@ -93,7 +92,7 @@ public class PlatformCarrierScheduleQueryController {
|
||||
public ApiResponse<Detail> create(@AuthenticationPrincipal AuthenticatedUser user,
|
||||
@RequestHeader("Idempotency-Key") String key,
|
||||
@Valid @RequestBody CreateRequest request) {
|
||||
FirstlegScopeGuard.platform(user, request.customerId());
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.create(user.userId(), user.username(), key, request));
|
||||
}
|
||||
|
||||
@@ -105,7 +104,7 @@ public class PlatformCarrierScheduleQueryController {
|
||||
@PathVariable @Positive long queryId,
|
||||
@RequestHeader("Idempotency-Key") String key,
|
||||
@Valid @RequestBody ManualResultRequest request) {
|
||||
FirstlegScopeGuard.platform(user, service.customerId(queryId));
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.attachManualResult(queryId, user.userId(), user.username(), key, request));
|
||||
}
|
||||
|
||||
@@ -118,7 +117,7 @@ public class PlatformCarrierScheduleQueryController {
|
||||
@PathVariable @Positive long queryId,
|
||||
@RequestHeader("Idempotency-Key") String key,
|
||||
@Valid @RequestBody ManualNodeRequest request) {
|
||||
FirstlegScopeGuard.platform(user, service.customerId(queryId));
|
||||
FirstlegScopeGuard.platform(user);
|
||||
return ApiResponse.success(service.addManualNode(queryId, user.userId(), user.username(), key, request));
|
||||
}
|
||||
}
|
||||
|
||||
+121
@@ -1,17 +1,138 @@
|
||||
package com.baiyue.firstleg.portschedule.controller;
|
||||
|
||||
import com.baiyue.common.audit.BusinessAudit;
|
||||
import com.baiyue.common.core.api.PageResult;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.portschedule.dto.CarrierScheduleQueryDtos.*;
|
||||
import com.baiyue.firstleg.portschedule.service.CarrierScheduleQueryService;
|
||||
import jakarta.validation.Validation;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class PlatformCarrierScheduleQueryControllerContractTest {
|
||||
private final CarrierScheduleQueryService service = mock(CarrierScheduleQueryService.class);
|
||||
private final PlatformCarrierScheduleQueryController controller = new PlatformCarrierScheduleQueryController();
|
||||
private final AuthenticatedUser platform = user("PLATFORM", 0L);
|
||||
private final long queryId = 88L;
|
||||
private final Query query = new Query(0L, 12L, 13L, null, null, null, null, null, null);
|
||||
private final CreateRequest create = new CreateRequest(0, 0L, 12L, 13L, "COSCO", "CONTAINER", "TEST1234567", null);
|
||||
private final ManualResultRequest result = new ManualResultRequest(1, 99L, "人工关联船期");
|
||||
private final ManualNodeRequest node = new ManualNodeRequest(1, "DEPARTED_ORIGIN_PORT",
|
||||
LocalDateTime.of(2026, 9, 30, 10, 0), null, LocalDateTime.of(2026, 9, 30, 10, 0),
|
||||
"CNSHA", "上海港", "人工确认实际开航");
|
||||
private Detail detail;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
ReflectionTestUtils.setField(controller, "service", service);
|
||||
Task task = mock(Task.class);
|
||||
when(task.customerId()).thenReturn(0L);
|
||||
detail = new Detail(new QueryView(task, false), List.of(), List.of(), List.of());
|
||||
// Platform bookings persist task ownership as zero. The former positive-target guard rejected it.
|
||||
when(service.customerId(anyLong())).thenReturn(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformCanFilterZeroScopeTasks() {
|
||||
var page = new PageResult<QueryView>(List.of(detail.query()), 1, 20, 1, 1);
|
||||
when(service.page(query)).thenReturn(page);
|
||||
assertThat(controller.page(platform, query).data()).isSameAs(page);
|
||||
verify(service).page(query);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformCanReadZeroScopeTaskDetail() {
|
||||
when(service.detail(queryId)).thenReturn(detail);
|
||||
assertThat(controller.detail(platform, queryId).data()).isSameAs(detail);
|
||||
assertThat(detail.query().query().customerId()).isZero();
|
||||
verify(service).detail(queryId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformCanReadZeroScopeTaskHistory() {
|
||||
var history = List.of(new History(5L, "OUTBOUND_CONFIRM", 0, 1, "自动离仓",
|
||||
7L, "SYSTEM", LocalDateTime.of(2026, 9, 30, 10, 0)));
|
||||
when(service.history(queryId)).thenReturn(history);
|
||||
assertThat(controller.history(platform, queryId).data()).isSameAs(history);
|
||||
verify(service).history(queryId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformCanCreateZeroScopeTaskThroughNormalLinkValidation() {
|
||||
when(service.create(7L, "operator", "create-key", create)).thenReturn(detail);
|
||||
assertThat(controller.create(platform, "create-key", create).data()).isSameAs(detail);
|
||||
verify(service).create(7L, "operator", "create-key", create);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformCanAttachZeroScopeTaskManualResult() {
|
||||
when(service.attachManualResult(queryId, 7L, "operator", "result-key", result)).thenReturn(detail);
|
||||
assertThat(controller.manualResult(platform, queryId, "result-key", result).data()).isSameAs(detail);
|
||||
verify(service).attachManualResult(queryId, 7L, "operator", "result-key", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void platformCanAppendZeroScopeTaskManualNode() {
|
||||
when(service.addManualNode(queryId, 7L, "operator", "node-key", node)).thenReturn(detail);
|
||||
assertThat(controller.manualNode(platform, queryId, "node-key", node).data()).isSameAs(detail);
|
||||
verify(service).addManualNode(queryId, 7L, "operator", "node-key", node);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nonPlatformPrincipalsAreRejectedBeforeAnyResourceLookupOrMutation() {
|
||||
for (AuthenticatedUser denied : Arrays.asList(null, user("MERCHANT", 42L), user("PDA", 0L),
|
||||
user("PLATFORM", 42L), user("PLATFORM", null))) {
|
||||
List<Runnable> actions = List.of(
|
||||
() -> controller.carriers(denied),
|
||||
() -> controller.containerActualFacts(denied, List.of(13L)),
|
||||
() -> controller.page(denied, query),
|
||||
() -> controller.detail(denied, queryId),
|
||||
() -> controller.history(denied, queryId),
|
||||
() -> controller.create(denied, "create-key", create),
|
||||
() -> controller.manualResult(denied, queryId, "result-key", result),
|
||||
() -> controller.manualNode(denied, queryId, "node-key", node));
|
||||
for (Runnable action : actions) {
|
||||
assertThatThrownBy(action::run).isInstanceOfSatisfying(BizException.class,
|
||||
failure -> assertThat(failure.getResultCode()).isEqualTo(ResultCode.FORBIDDEN));
|
||||
}
|
||||
}
|
||||
verifyNoInteractions(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestValidationAllowsPlatformZeroAndRejectsNegativeScope() {
|
||||
try (var factory = Validation.buildDefaultValidatorFactory()) {
|
||||
var validator = factory.getValidator();
|
||||
assertThat(validator.validate(query)).isEmpty();
|
||||
assertThat(validator.validate(create)).isEmpty();
|
||||
assertThat(validator.validate(new Query(-1L, 12L, 13L, null, null, null, null, null, null)))
|
||||
.anyMatch(violation -> violation.getPropertyPath().toString().equals("customerId"));
|
||||
assertThat(validator.validate(new CreateRequest(0, -1L, 12L, 13L, "COSCO", "CONTAINER", "TEST1234567", null)))
|
||||
.anyMatch(violation -> violation.getPropertyPath().toString().equals("customerId"));
|
||||
}
|
||||
}
|
||||
|
||||
private static AuthenticatedUser user(String appCode, Long scope) {
|
||||
return new AuthenticatedUser(7L, scope, 0L, null, "operator", appCode, "WEB", Set.of());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exposesDedicatedTaskResourceAndLeastPrivilegeActions() {
|
||||
assertThat(PlatformCarrierScheduleQueryController.class.getAnnotation(RequestMapping.class).value())
|
||||
|
||||
Reference in New Issue
Block a user