This commit is contained in:
2026-08-08 14:26:02 +08:00
parent 13a2b90e98
commit 355b8629d9
7 changed files with 1282 additions and 23 deletions
@@ -0,0 +1,111 @@
package com.baiyue.firstleg.repository;
import com.baiyue.firstleg.controller.PlatformTruckAppointmentController;
import com.baiyue.firstleg.oceanbooking.controller.PlatformOceanBookingController;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.access.prepost.PreAuthorize;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
class Phase3BookingTruckPermissionSqlContractTest {
private static final Pattern AUTHORITY = Pattern.compile("hasAuthority\\('([^']+)'\\)");
private static final Pattern OCEAN_CODE = Pattern.compile("platform:firstleg:ocean-booking:[a-z-]+");
private static final Pattern TRUCK_CODE = Pattern.compile("platform:firstleg:truck-appointment:[a-z-]+");
private static final Set<String> OCEAN_PERMISSIONS = Set.of(
"platform:firstleg:ocean-booking:list",
"platform:firstleg:ocean-booking:view",
"platform:firstleg:ocean-booking:create",
"platform:firstleg:ocean-booking:edit",
"platform:firstleg:ocean-booking:submit",
"platform:firstleg:ocean-booking:confirm",
"platform:firstleg:ocean-booking:release",
"platform:firstleg:ocean-booking:cancel",
"platform:firstleg:ocean-booking:change",
"platform:firstleg:ocean-booking:allocate");
private static final Set<String> TRUCK_PERMISSIONS = Set.of(
"platform:firstleg:truck-appointment:list",
"platform:firstleg:truck-appointment:view",
"platform:firstleg:truck-appointment:create",
"platform:firstleg:truck-appointment:edit",
"platform:firstleg:truck-appointment:operate");
@Test
void controllerAuthoritiesExactlyMatchModuleAndUnifiedPermissionCodes() throws Exception {
assertThat(authorities(PlatformOceanBookingController.class)).isEqualTo(OCEAN_PERMISSIONS);
assertThat(authorities(PlatformTruckAppointmentController.class)).isEqualTo(TRUCK_PERMISSIONS);
String oceanModule = resource("db/phase3-ocean-booking-schema.sql");
String truckModule = resource("db/phase3-truck-appointment-schema.sql");
String unified = unifiedSql();
assertThat(codes(oceanModule, OCEAN_CODE)).isEqualTo(OCEAN_PERMISSIONS);
assertThat(codes(truckModule, TRUCK_CODE)).isEqualTo(TRUCK_PERMISSIONS);
assertThat(codes(unified, OCEAN_CODE)).isEqualTo(OCEAN_PERMISSIONS);
assertThat(codes(unified, TRUCK_CODE)).isEqualTo(TRUCK_PERMISSIONS);
}
@Test
void unifiedSqlLocksCanonicalIdsScopeParentsAndRoleGrants() throws Exception {
String sql = unifiedSql();
assertThat(sql)
.contains("UNIQUE KEY uk_permission_scope_code (tenant_id, app_code, permission_code)")
.contains("CREATE PROCEDURE assert_phase3_booking_truck_permission_ids_available()")
.contains("三期订舱/拖车权限迁移失败:5102140-5102154 ID 或权限码被其他语义占用")
.contains("(5102140,0,'PLATFORM','MENU',5102000,'platform:firstleg:ocean-booking:list'")
.contains("(5102149,0,'PLATFORM','BUTTON',5102140,'platform:firstleg:ocean-booking:allocate'")
.contains("(5102150,0,'PLATFORM','MENU',5102000,'platform:firstleg:truck-appointment:list'")
.contains("(5102154,0,'PLATFORM','BUTTON',5102150,'platform:firstleg:truck-appointment:operate'")
.contains("role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')")
.contains("CREATE PROCEDURE assert_phase3_booking_truck_permissions()")
.contains("统一迁移失败:三期订舱/拖车 15 项权限映射不完整")
.contains("统一迁移失败:三期订舱/拖车角色授权不完整")
.contains("CALL assert_phase3_booking_truck_permissions()")
.contains("DROP PROCEDURE IF EXISTS assert_phase3_booking_truck_permissions;");
}
private static Set<String> authorities(Class<?> controller) {
Set<String> result = new LinkedHashSet<>();
Arrays.stream(controller.getDeclaredMethods())
.map(method -> method.getAnnotation(PreAuthorize.class))
.filter(annotation -> annotation != null)
.map(PreAuthorize::value)
.forEach(expression -> {
Matcher matcher = AUTHORITY.matcher(expression);
assertThat(matcher.find()).as(expression).isTrue();
result.add(matcher.group(1));
});
return result;
}
private static Set<String> codes(String sql, Pattern pattern) {
Set<String> result = new LinkedHashSet<>();
Matcher matcher = pattern.matcher(sql);
while (matcher.find()) {
result.add(matcher.group());
}
return result;
}
private static String resource(String path) throws Exception {
return new ClassPathResource(path).getContentAsString(StandardCharsets.UTF_8);
}
private static String unifiedSql() throws Exception {
Path path = Path.of("deploy/sql/头程数据库统一增量-MySQL8.sql");
if (!Files.exists(path)) {
path = Path.of("../deploy/sql/头程数据库统一增量-MySQL8.sql").normalize();
}
return Files.readString(path, StandardCharsets.UTF_8);
}
}
@@ -0,0 +1,41 @@
package com.baiyue.firstleg.repository;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.assertj.core.api.Assertions.assertThat;
class ServiceCenterUnifiedSqlContractTest {
@Test
void unifiedMigrationContainsServiceCenterSchemaAndGrants() throws Exception {
Path working = Path.of(System.getProperty("user.dir"));
Path path = working.resolve("deploy/sql/头程数据库统一增量-MySQL8.sql");
if (!Files.exists(path)) {
path = working.resolve("../deploy/sql/头程数据库统一增量-MySQL8.sql").normalize();
}
String sql = Files.readString(path, StandardCharsets.UTF_8);
assertThat(sql)
.contains("CREATE TABLE IF NOT EXISTS fl_ocean_booking")
.contains("CREATE TABLE IF NOT EXISTS fl_ocean_booking_party")
.contains("CREATE TABLE IF NOT EXISTS fl_ocean_booking_container_requirement")
.contains("CREATE TABLE IF NOT EXISTS fl_ocean_booking_container_allocation")
.contains("CREATE TABLE IF NOT EXISTS fl_ocean_booking_status_history")
.contains("CREATE TABLE IF NOT EXISTS fl_ocean_booking_operation")
.contains("CREATE TABLE IF NOT EXISTS fl_truck_appointment")
.contains("CREATE TABLE IF NOT EXISTS fl_truck_appointment_history")
.contains("CREATE TABLE IF NOT EXISTS fl_truck_appointment_attachment")
.contains("CREATE TABLE IF NOT EXISTS fl_truck_appointment_operation")
.contains("5102140")
.contains("5102149")
.contains("5102150")
.contains("5102154")
.contains("PLATFORM_ADMIN")
.contains("ORDER_OPERATOR")
.contains("assert_phase3_service_center_schema()")
.contains("CALL assert_phase3_service_center_schema();");
}
}
@@ -40,7 +40,7 @@ class TruckAppointmentServiceTest {
when(repository.find(1L)).thenReturn(e);
when(repository.transition(1L, "DRAFT", "PENDING_DISPATCH", 0)).thenReturn(1);
var result = service.action(1L, "submit", new ActionRequest(0, "提交"), "idem-1", "hash-1", 7L, "admin");
var result = service.action(1L, "SUBMIT", new ActionRequest(0, "提交"), "idem-1", "hash-1", 7L, "admin");
assertThat(result.getStatus()).isEqualTo(TruckAppointmentStatus.PENDING_DISPATCH);
assertThat(result.getVersion()).isEqualTo(1);