feat(firstleg): implement container customs workflow

This commit is contained in:
2026-08-28 16:48:50 +08:00
parent bb9673358d
commit 0ea4aeecc9
92 changed files with 3165 additions and 2754 deletions
@@ -0,0 +1,152 @@
-- 三期整柜报关权限资源;在一期平台 RBAC 初始化后执行,可重复部署。
-- Client 退税资料复用 merchant:order:view/edit;买单无客户入口,不新增客户报关权限。
USE baiyue_iam;
SET NAMES utf8mb4;
DROP PROCEDURE IF EXISTS guard_phase3_container_customs_permission_ids;
DELIMITER $$
CREATE PROCEDURE guard_phase3_container_customs_permission_ids()
BEGIN
IF EXISTS (
SELECT 1
FROM iam_permission
WHERE (id = 5102170 AND permission_code <> 'platform:firstleg:customs:list')
OR (id = 5102172 AND permission_code <> 'platform:firstleg:customs:manage')
OR (permission_code = 'platform:firstleg:customs:list' AND id <> 5102170)
OR (permission_code = 'platform:firstleg:customs:manage' AND id <> 5102172)
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '整柜报关权限固定 ID 或权限码已被其他语义占用';
END IF;
END$$
DELIMITER ;
CALL guard_phase3_container_customs_permission_ids();
DROP PROCEDURE IF EXISTS guard_phase3_container_customs_permission_ids;
-- 当前未上线,旧 cases 动作和装柜清单导出权限直接删除,不保留别名或双权限。
DELETE role_permission
FROM iam_role_permission role_permission
JOIN iam_permission permission
ON permission.id = role_permission.permission_id
AND permission.tenant_id = role_permission.tenant_id
WHERE permission.tenant_id = 0
AND permission.app_code = 'PLATFORM'
AND permission.permission_code IN (
'platform:firstleg:customs:view',
'platform:firstleg:customs:review',
'platform:firstleg:customs:submit',
'platform:firstleg:customs:receipt',
'platform:firstleg:loading-list:export'
);
DELETE FROM iam_permission
WHERE tenant_id = 0
AND app_code = 'PLATFORM'
AND permission_code IN (
'platform:firstleg:customs:view',
'platform:firstleg:customs:review',
'platform:firstleg:customs:submit',
'platform:firstleg:customs:receipt',
'platform:firstleg:loading-list:export'
);
INSERT INTO iam_permission
(id, tenant_id, app_code, permission_type, parent_id, permission_code,
permission_name, route_path, http_method, sort_no, status, built_in, deleted)
VALUES
(5102170, 0, 'PLATFORM', 'MENU', 5102000,
'platform:firstleg:customs:list', '整柜报关', '/first-leg/customs', 'GET',
100, 'ACTIVE', 1, 0),
(5102172, 0, 'PLATFORM', 'BUTTON', 5102170,
'platform:firstleg:customs:manage', '维护整柜报关', NULL, 'POST',
20, 'ACTIVE', 1, 0)
ON DUPLICATE KEY UPDATE
tenant_id = 0,
app_code = 'PLATFORM',
permission_type = VALUES(permission_type),
parent_id = VALUES(parent_id),
permission_name = VALUES(permission_name),
route_path = VALUES(route_path),
http_method = VALUES(http_method),
sort_no = VALUES(sort_no),
status = 'ACTIVE',
built_in = 1,
deleted = 0;
INSERT IGNORE INTO iam_role_permission (role_id, permission_id, tenant_id)
SELECT role.id, permission.id, 0
FROM iam_role role
JOIN iam_permission permission
ON permission.tenant_id = 0
AND permission.app_code = 'PLATFORM'
AND permission.permission_code IN (
'platform:firstleg:customs:list',
'platform:firstleg:customs:manage'
)
WHERE role.tenant_id = 0
AND role.app_code = 'PLATFORM'
AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR')
AND role.status = 'ACTIVE'
AND role.deleted = 0;
DROP PROCEDURE IF EXISTS assert_phase3_container_customs_permissions;
DELIMITER $$
CREATE PROCEDURE assert_phase3_container_customs_permissions()
BEGIN
IF (
SELECT COUNT(*)
FROM iam_permission
WHERE tenant_id = 0
AND app_code = 'PLATFORM'
AND status = 'ACTIVE'
AND deleted = 0
AND permission_code IN (
'platform:firstleg:customs:list',
'platform:firstleg:customs:manage'
)
) <> 2 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '整柜报关正式权限资源初始化不完整';
END IF;
IF EXISTS (
SELECT 1
FROM iam_permission
WHERE tenant_id = 0
AND app_code = 'PLATFORM'
AND permission_code IN (
'platform:firstleg:customs:view',
'platform:firstleg:customs:review',
'platform:firstleg:customs:submit',
'platform:firstleg:customs:receipt',
'platform:firstleg:loading-list:export'
)
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '整柜报关旧权限或装柜清单导出权限未清除';
END IF;
IF (
SELECT COUNT(*)
FROM iam_role_permission role_permission
JOIN iam_role role
ON role.id = role_permission.role_id
AND role.tenant_id = role_permission.tenant_id
JOIN iam_permission permission
ON permission.id = role_permission.permission_id
AND permission.tenant_id = role_permission.tenant_id
WHERE role.tenant_id = 0
AND role.app_code = 'PLATFORM'
AND role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR')
AND permission.permission_code IN (
'platform:firstleg:customs:list',
'platform:firstleg:customs:manage'
)
) <> 4 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '平台管理员或订单运营整柜报关权限授权不完整';
END IF;
END$$
DELIMITER ;
CALL assert_phase3_container_customs_permissions();
DROP PROCEDURE IF EXISTS assert_phase3_container_customs_permissions;
@@ -0,0 +1,70 @@
package com.baiyue.iam.repository;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import java.nio.charset.StandardCharsets;
import static org.assertj.core.api.Assertions.assertThat;
class ContainerCustomsPermissionSqlContractTest {
@Test
void registersOnlyTheFinalPlatformPermissionsAndDefaultRoles() throws Exception {
String sql = sql();
assertThat(sql).contains(
"5102170",
"5102172",
"platform:firstleg:customs:list",
"platform:firstleg:customs:manage",
"'/first-leg/customs'",
"role.role_code IN ('PLATFORM_ADMIN', 'ORDER_OPERATOR')",
"ON DUPLICATE KEY UPDATE",
"INSERT IGNORE INTO iam_role_permission",
"guard_phase3_container_customs_permission_ids",
"assert_phase3_container_customs_permissions",
"SIGNAL SQLSTATE '45000'"
);
}
@Test
void removesLegacyCaseAndLoadingListExportPermissionsWithoutAliases() throws Exception {
String sql = sql();
assertThat(sql).contains(
"DELETE role_permission",
"DELETE FROM iam_permission",
"'platform:firstleg:customs:view'",
"'platform:firstleg:customs:review'",
"'platform:firstleg:customs:submit'",
"'platform:firstleg:customs:receipt'",
"'platform:firstleg:loading-list:export'"
);
assertThat(sql).doesNotContain(
"hasAnyAuthority",
"merchant:customs",
"platform:firstleg:customs:cases"
);
}
@Test
void reusesOrderPermissionsForMerchantTaxRefundDocuments() throws Exception {
String sql = sql();
assertThat(sql).contains(
"merchant:order:view/edit",
"买单无客户入口"
);
assertThat(sql).doesNotContain(
"'merchant:order:view'",
"'merchant:order:edit'",
"'MERCHANT'"
);
}
private String sql() throws Exception {
return new ClassPathResource("db/phase3-container-customs-permissions.sql")
.getContentAsString(StandardCharsets.UTF_8);
}
}