This commit is contained in:
2026-08-20 09:10:30 +08:00
parent 064247a8c9
commit f227cf05a3
32 changed files with 268 additions and 58 deletions
@@ -41,7 +41,7 @@ public class PlatformCarrierScheduleQueryController {
@Operation(summary = "查询六家船公司合规适配策略")
@GetMapping("/carriers")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule-query:view')")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule-query:list')")
public ApiResponse<List<CarrierDescriptor>> carriers(@AuthenticationPrincipal AuthenticatedUser user) {
FirstlegScopeGuard.platform(user);
return ApiResponse.success(service.carriers());
@@ -57,7 +57,7 @@ public class PlatformCarrierScheduleQueryController {
}
@GetMapping("/{queryId}")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule-query:view')")
@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));
@@ -65,7 +65,7 @@ public class PlatformCarrierScheduleQueryController {
}
@GetMapping("/{queryId}/history")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule-query:view')")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule-query:list')")
public ApiResponse<List<CarrierScheduleQueryHistoryEntity>> history(
@AuthenticationPrincipal AuthenticatedUser user, @PathVariable @Positive long queryId) {
FirstlegScopeGuard.platform(user, service.customerId(queryId));
@@ -47,7 +47,7 @@ public class PlatformPortScheduleController {
}
@GetMapping("/{portScheduleId}")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule:view')")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule:list')")
public ApiResponse<Detail> detail(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long portScheduleId) {
FirstlegScopeGuard.platform(user);
@@ -55,7 +55,7 @@ public class PlatformPortScheduleController {
}
@GetMapping("/{portScheduleId}/history")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule:view')")
@PreAuthorize("hasAuthority('platform:firstleg:port-schedule:list')")
public ApiResponse<List<PortScheduleHistoryEntity>> history(@AuthenticationPrincipal AuthenticatedUser user,
@PathVariable @Positive long portScheduleId) {
FirstlegScopeGuard.platform(user);
@@ -150,22 +150,29 @@ FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询历史
DELIMITER ;
USE baiyue_iam;
DELETE role_permission FROM iam_role_permission role_permission
JOIN iam_permission permission ON permission.id=role_permission.permission_id
WHERE permission.tenant_id=0
AND permission.permission_code='platform:firstleg:port-schedule-query:view';
DELETE FROM iam_permission
WHERE tenant_id=0 AND permission_code='platform:firstleg:port-schedule-query:view';
DROP PROCEDURE IF EXISTS assert_carrier_schedule_query_permission_ids;
DELIMITER $$
CREATE PROCEDURE assert_carrier_schedule_query_permission_ids()
BEGIN
IF EXISTS (
SELECT 1 FROM iam_permission
WHERE (id BETWEEN 5102184 AND 5102188
WHERE (id IN (5102184,5102186,5102187,5102188)
AND permission_code <> CASE id
WHEN 5102184 THEN 'platform:firstleg:port-schedule-query:list'
WHEN 5102185 THEN 'platform:firstleg:port-schedule-query:view'
WHEN 5102186 THEN 'platform:firstleg:port-schedule-query:create'
WHEN 5102187 THEN 'platform:firstleg:port-schedule-query:attempt'
WHEN 5102188 THEN 'platform:firstleg:port-schedule-query:manual'
END)
OR id=5102185
OR (permission_code LIKE 'platform:firstleg:port-schedule-query:%'
AND (tenant_id<>0 OR app_code<>'PLATFORM' OR id NOT BETWEEN 5102184 AND 5102188))
AND (tenant_id<>0 OR app_code<>'PLATFORM' OR id NOT IN (5102184,5102186,5102187,5102188)))
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT='三期船司查询权限迁移失败:5102184-5102188 ID或权限码被占用';
@@ -180,7 +187,6 @@ INSERT INTO iam_permission
route_path,http_method,sort_no,status,built_in,deleted)
VALUES
(5102184,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:list','查询船司任务',NULL,'GET',40,'ACTIVE',1,0),
(5102185,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:view','查看船司任务',NULL,'GET',50,'ACTIVE',1,0),
(5102186,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:create','创建船司任务',NULL,'POST',60,'ACTIVE',1,0),
(5102187,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:attempt','执行船司查询',NULL,'POST',70,'ACTIVE',1,0),
(5102188,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule-query:manual','录入人工结果',NULL,'POST',80,'ACTIVE',1,0)
@@ -208,7 +214,7 @@ BEGIN
END IF;
IF (SELECT COUNT(*) FROM baiyue_iam.iam_permission
WHERE tenant_id=0 AND id BETWEEN 5102184 AND 5102188
AND permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 5 THEN
AND permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 4 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务权限未完整创建';
END IF;
IF (SELECT COUNT(*)
@@ -217,7 +223,7 @@ BEGIN
JOIN baiyue_iam.iam_permission permission ON permission.id=role_permission.permission_id
WHERE role.tenant_id=0 AND role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')
AND permission.tenant_id=0
AND permission.permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 10 THEN
AND permission.permission_code LIKE 'platform:firstleg:port-schedule-query:%') <> 8 THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='船司查询任务角色授权未完整创建';
END IF;
END$$
@@ -122,21 +122,28 @@ BEGIN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT='港务船期历史不可删除';
DELIMITER ;
USE baiyue_iam;
DELETE role_permission FROM iam_role_permission role_permission
JOIN iam_permission permission ON permission.id=role_permission.permission_id
WHERE permission.tenant_id=0
AND permission.permission_code='platform:firstleg:port-schedule:view';
DELETE FROM iam_permission
WHERE tenant_id=0 AND permission_code='platform:firstleg:port-schedule:view';
DROP PROCEDURE IF EXISTS assert_port_schedule_permission_ids_available;
DELIMITER $$
CREATE PROCEDURE assert_port_schedule_permission_ids_available()
BEGIN
IF EXISTS (
SELECT 1 FROM iam_permission
WHERE (id BETWEEN 5102180 AND 5102183
WHERE (id IN (5102180,5102182,5102183)
AND permission_code <> CASE id
WHEN 5102180 THEN 'platform:firstleg:port-schedule:list'
WHEN 5102181 THEN 'platform:firstleg:port-schedule:view'
WHEN 5102182 THEN 'platform:firstleg:port-schedule:create'
WHEN 5102183 THEN 'platform:firstleg:port-schedule:edit'
END)
OR id=5102181
OR (permission_code LIKE 'platform:firstleg:port-schedule:%'
AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR id NOT BETWEEN 5102180 AND 5102183))
AND (tenant_id <> 0 OR app_code <> 'PLATFORM' OR id NOT IN (5102180,5102182,5102183)))
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '三期港务船期权限迁移失败:5102180-5102183 ID 或权限码被其他语义占用';
@@ -151,7 +158,6 @@ INSERT INTO iam_permission
route_path,http_method,sort_no,status,built_in,deleted)
VALUES
(5102180,0,'PLATFORM','MENU',5102000,'platform:firstleg:port-schedule:list','港务船期','/first-leg/port-schedules','GET',120,'ACTIVE',1,0),
(5102181,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule:view','查看港务船期',NULL,'GET',10,'ACTIVE',1,0),
(5102182,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule:create','新增港务船期',NULL,'POST',20,'ACTIVE',1,0),
(5102183,0,'PLATFORM','BUTTON',5102180,'platform:firstleg:port-schedule:edit','更新港务船期',NULL,'PUT',30,'ACTIVE',1,0)
ON DUPLICATE KEY UPDATE tenant_id=0,app_code='PLATFORM',permission_type=VALUES(permission_type),
@@ -7,11 +7,13 @@ paths:
/api/platform/v1/carrier-schedule-queries/carriers:
get:
summary: List official carrier sources and compliant access modes
x-permission: platform:firstleg:port-schedule-query:list
responses:
'200': {description: MATSON, SML, ZIM, COSCO, EMC and HEDE descriptors}
/api/platform/v1/carrier-schedule-queries:
get:
summary: Query carrier schedule tasks
x-permission: platform:firstleg:port-schedule-query:list
parameters:
- {name: customerId, in: query, schema: {type: integer, format: int64, minimum: 1}}
- {name: oceanBookingId, in: query, schema: {type: integer, format: int64, minimum: 1}}
@@ -26,6 +28,7 @@ paths:
'200': {description: Query tasks with source, freshness and rate-limit fields}
post:
summary: Create a query task linked to a real booking and allocated container
x-permission: platform:firstleg:port-schedule-query:create
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
@@ -38,18 +41,21 @@ paths:
parameters: [{$ref: '#/components/parameters/QueryId'}]
get:
summary: Get task, immutable attempts and history
x-permission: platform:firstleg:port-schedule-query:list
responses:
'200': {description: Stable task detail}
/api/platform/v1/carrier-schedule-queries/{queryId}/history:
parameters: [{$ref: '#/components/parameters/QueryId'}]
get:
summary: Get immutable task history
x-permission: platform:firstleg:port-schedule-query:list
responses:
'200': {description: CREATE, ATTEMPT and MANUAL_RESULT history}
/api/platform/v1/carrier-schedule-queries/{queryId}/attempts:
parameters: [{$ref: '#/components/parameters/QueryId'}]
post:
summary: Execute the compliant carrier strategy; never bypass captcha or dynamic pages
x-permission: platform:firstleg:port-schedule-query:attempt
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
@@ -62,6 +68,7 @@ paths:
parameters: [{$ref: '#/components/parameters/QueryId'}]
post:
summary: Link a validated manual port-schedule observation without pretending automated success
x-permission: platform:firstleg:port-schedule-query:manual
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
@@ -7,6 +7,7 @@ paths:
/api/platform/v1/port-schedules:
get:
summary: Query schedules by customer, ocean booking, allocated container or expiry
x-permission: platform:firstleg:port-schedule:list
parameters:
- {name: customerId, in: query, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
- {name: oceanBookingId, in: query, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
@@ -20,6 +21,7 @@ paths:
description: HTTP 200 business envelope containing a page of schedule views
post:
summary: Create a source observation linked to a real ocean booking
x-permission: platform:firstleg:port-schedule:create
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
@@ -32,10 +34,12 @@ paths:
parameters: [{$ref: '#/components/parameters/PortScheduleId'}]
get:
summary: Get schedule and immutable history
x-permission: platform:firstleg:port-schedule:list
responses:
'200': {description: Schedule detail with computed expired flag}
put:
summary: Update source observation with optimistic locking
x-permission: platform:firstleg:port-schedule:edit
parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
requestBody:
required: true
@@ -48,6 +52,7 @@ paths:
parameters: [{$ref: '#/components/parameters/PortScheduleId'}]
get:
summary: Query immutable source observation history
x-permission: platform:firstleg:port-schedule:list
responses:
'200': {description: Ordered CREATE and UPDATE history records}
components:
@@ -17,10 +17,10 @@ class PlatformCarrierScheduleQueryControllerContractTest {
assertThat(PlatformCarrierScheduleQueryController.class.getAnnotation(RequestMapping.class).value())
.containsExactly("/api/platform/v1/carrier-schedule-queries");
Map<String, String> permissions = Map.of(
"carriers", "platform:firstleg:port-schedule-query:view",
"carriers", "platform:firstleg:port-schedule-query:list",
"page", "platform:firstleg:port-schedule-query:list",
"detail", "platform:firstleg:port-schedule-query:view",
"history", "platform:firstleg:port-schedule-query:view",
"detail", "platform:firstleg:port-schedule-query:list",
"history", "platform:firstleg:port-schedule-query:list",
"create", "platform:firstleg:port-schedule-query:create",
"attempt", "platform:firstleg:port-schedule-query:attempt",
"manualResult", "platform:firstleg:port-schedule-query:manual");
@@ -18,8 +18,8 @@ class PlatformPortScheduleControllerContractTest {
.containsExactly("/api/platform/v1/port-schedules");
Map<String, String> permissions = Map.of(
"page", "platform:firstleg:port-schedule:list",
"detail", "platform:firstleg:port-schedule:view",
"history", "platform:firstleg:port-schedule:view",
"detail", "platform:firstleg:port-schedule:list",
"history", "platform:firstleg:port-schedule:list",
"create", "platform:firstleg:port-schedule:create",
"update", "platform:firstleg:port-schedule:edit");
permissions.forEach((name, permission) -> {
@@ -37,9 +37,11 @@ class PortScheduleSqlContractTest {
@Test
void reservesDedicatedPlatformPermissionsForBothOperationalRoles() throws Exception {
assertThat(sql())
.contains("5102180 AND 5102183")
.contains("id IN (5102180,5102182,5102183)")
.contains("platform:firstleg:port-schedule:list")
.contains("platform:firstleg:port-schedule:view")
.contains("permission.permission_code='platform:firstleg:port-schedule:view'")
.contains("permission_code='platform:firstleg:port-schedule:view'")
.doesNotContain("'platform:firstleg:port-schedule:view','查看港务船期'")
.contains("platform:firstleg:port-schedule:create")
.contains("platform:firstleg:port-schedule:edit")
.contains("role.role_code IN ('PLATFORM_ADMIN','ORDER_OPERATOR')");