fix: restore matchmaker workbench permissions

This commit is contained in:
WorkBuddy
2026-10-25 09:30:13 +08:00
parent 692d71696a
commit f6744d3f91
6 changed files with 171 additions and 26 deletions
@@ -0,0 +1,15 @@
-- V074: 恢复红娘工作台六项卡片及已确认的业务操作权限。
-- 仅使用既有 permission code;不授予资格原始佐证、咨询备注查看或代用户决策权限。
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
CROSS JOIN permissions p
WHERE r.code IN ('matchmaker', 'matchmaker_leader')
AND p.code IN (
'workbench.view', 'review.view', 'review.process', 'local_eligibility.review',
'counseling.service.view', 'counseling.service.assign', 'counseling.service.process',
'counseling.note.create', 'service_appointment.view', 'service_appointment.assign',
'service_appointment.confirm', 'service_appointment.complete', 'activity.view',
'activity.review_registration', 'analytics.view'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;
@@ -45,8 +45,8 @@ class MigrationValidationTest {
// V067 工作台聚合权限与红娘角色边界;V068/V069 红娘工作台权限;
// V070 收回红娘的咨询、活动、预约、统计权限;
// V071 收回红娘的资料审核、本地资格审核权限。
// V073 对齐 reviewer、activity_manager、risk_operator、organization_admin 最小权限。
private static final int LATEST_MIGRATION = 73;
// V073 对齐其他运营角色;V074 恢复红娘工作台六卡片及操作权限。
private static final int LATEST_MIGRATION = 74;
private static final String MIGRATION_DIR =
"backend/src/main/resources/db/migration/";
@@ -0,0 +1,110 @@
package com.danyang.match.platform.deployment;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
class V12MigrationContractTest {
private static final Path DIR = Path.of("backend/src/main/resources/db/migration");
@Test
void v12MigrationsAndLifecycleContractsExist() throws Exception {
assertMigration("V062__add_v12_user_account_lifecycle.sql");
assertMigration("V063__add_v12_import_batches.sql");
assertMigration("V064__add_v12_consent_and_service_state.sql");
assertMigration("V065__add_v12_registration_applications.sql");
assertMigration("V066__grant_user_onboarding_permission.sql");
String lifecycle = Files.readString(DIR.resolve("V062__add_v12_user_account_lifecycle.sql"));
assertAll(
() -> assertTrue(lifecycle.contains("profile_revision")),
() -> assertTrue(lifecycle.contains("published_profile_revision")),
() -> assertTrue(lifecycle.contains("user_credentials")),
() -> assertTrue(lifecycle.contains("must_change_password")),
() -> assertFalse(lifecycle.toLowerCase().contains("plaintext_password"))
);
String imports = Files.readString(DIR.resolve("V063__add_v12_import_batches.sql"));
assertAll(
() -> assertTrue(imports.contains("user_import_batches")),
() -> assertTrue(imports.contains("user_import_rows")),
() -> assertTrue(imports.contains("idempotency_key"))
);
String consent = Files.readString(DIR.resolve("V064__add_v12_consent_and_service_state.sql"));
assertAll(
() -> assertTrue(consent.contains("user_consent_states")),
() -> assertTrue(consent.contains("user_service_states")),
() -> assertTrue(consent.contains("service_active"))
);
}
@Test
void lifecycleMigrationsDoNotDropExistingObjects() throws Exception {
List<String> names = List.of(
"V062__add_v12_user_account_lifecycle.sql",
"V063__add_v12_import_batches.sql",
"V064__add_v12_consent_and_service_state.sql",
"V065__add_v12_registration_applications.sql",
"V066__grant_user_onboarding_permission.sql");
for (String name : names) {
String sql = Files.readString(DIR.resolve(name)).toUpperCase();
assertFalse(sql.contains("DROP TABLE"), name);
assertFalse(sql.contains("DROP COLUMN"), name);
}
}
@Test
void matchmakerCrossDomainPermissionsAreRevoked() throws Exception {
String sql = Files.readString(DIR.resolve("V070__remove_matchmaker_cross_domain_permissions.sql"));
assertAll(
() -> assertTrue(sql.contains("r.code IN ('matchmaker', 'matchmaker_leader')")),
() -> assertTrue(sql.contains("p.code LIKE 'counseling.%'")),
() -> assertTrue(sql.contains("p.code LIKE 'service_appointment.%'")),
() -> assertTrue(sql.contains("'activity.view'")),
() -> assertTrue(sql.contains("'activity.checkin'")),
() -> assertTrue(sql.contains("'activity.review_registration'")),
() -> assertTrue(sql.contains("'analytics.view'"))
);
}
@Test
void matchmakerReviewPermissionsAreRevoked() throws Exception {
String sql = Files.readString(DIR.resolve("V071__remove_matchmaker_review_permissions.sql"));
assertAll(
() -> assertTrue(sql.contains("r.code IN ('matchmaker', 'matchmaker_leader')")),
() -> assertTrue(sql.contains("'review.view'")),
() -> assertTrue(sql.contains("'review.process'")),
() -> assertTrue(sql.contains("'local_eligibility.review'"))
);
}
@Test
void matchmakerWorkbenchPermissionsAreRestoredWithoutSensitiveAccess() throws Exception {
Path migration = DIR.resolve("V074__restore_matchmaker_workbench_permissions.sql");
assertTrue(Files.exists(migration));
String sql = Files.readString(migration);
assertAll(
() -> assertTrue(sql.contains("r.code IN ('matchmaker', 'matchmaker_leader')")),
() -> assertTrue(sql.contains("'workbench.view'")),
() -> assertTrue(sql.contains("'review.process'")),
() -> assertTrue(sql.contains("'local_eligibility.review'")),
() -> assertTrue(sql.contains("'counseling.service.assign'")),
() -> assertTrue(sql.contains("'service_appointment.confirm'")),
() -> assertTrue(sql.contains("'service_appointment.complete'")),
() -> assertFalse(sql.contains("'local_eligibility.evidence.view'")),
() -> assertFalse(sql.contains("'counseling.note.view'")),
() -> assertFalse(sql.contains("'matchmaker.assign'")),
() -> assertTrue(sql.contains("ON CONFLICT (role_id, permission_id) DO NOTHING"))
);
}
private static void assertMigration(String name) {
assertTrue(Files.exists(DIR.resolve(name)), "缺少迁移: " + name);
}
}