修复能力:统一DMS禁用态门禁能力声明

capabilities 中 manualRevokeAvailable 此前硬编码为 true,在 dms.enabled=false 时
仍向用户表示真实外部门禁撤权可执行。

- manualRevokeAvailable 与 dms.enabled 保持一致
- 新增 localRevokeGovernanceAvailable 与 revokeMode,明确区分外部派发与本地排队治理,
  本地授权状态治理与安全补偿路径继续可用,撤权入口不被移除
- 前端门禁能力默认按外部通道不可用初始化,接口返回前不向用户承诺不存在的能力
- 门禁服务未启用时注销确认文案如实说明仅完成本地待注销登记

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wangzhibiaoandCursor committed 2026-09-22 17:04:50 +08:00
1 parent b7a9b89ec5
commit fb5834bb1e
3 files changed
+62 -5

No files matched your search

@@ -224,7 +224,13 @@ public class TrainAccessManagementController extends BaseController
Map<String, Object> data = new HashMap<>();
data.put("dmsEnabled", dmsEnabled);
data.put("manualIssueAvailable", dmsEnabled);
data.put("manualRevokeAvailable", true);
// FIX-07: 能力声明必须与真实可执行能力一致。DMS 禁用时无法向厂商发起真实撤权,
// 不得再硬编码 true 向用户表示外部门禁撤权可执行。
data.put("manualRevokeAvailable", dmsEnabled);
// 本地授权状态治理 (草稿注销 / 排队待注销) 不依赖外部通道,始终可用,
// 撤权入口因此保留,但语义由 revokeMode 明确区分,避免破坏安全补偿路径。
data.put("localRevokeGovernanceAvailable", true);
data.put("revokeMode", dmsEnabled ? "EXTERNAL_DISPATCH" : "LOCAL_QUEUE_ONLY");
return success(data);
}
@@ -137,5 +137,49 @@ public class AccommodationAndAuthApiControllerContractTest
Method revokeMethod = clazz.getMethod("revokeAuth", Long.class);
assertEquals("@ss.hasPermi('train:access:credential')", revokeMethod.getAnnotation(PreAuthorize.class).value());
}
/**
* FIX-07: 门禁能力声明必须与真实可执行能力一致。
* DMS 禁用时不得向用户表示真实外部门禁撤权可执行;
* 同时本地授权状态治理能力必须继续声明为可用,避免破坏安全补偿路径。
*/
@Test
public void testFix07_CapabilitiesMatchRealExecutableAbility() throws Exception
{
TrainAccessManagementController controller = new TrainAccessManagementController();
com.ruoyi.train.integration.dms.config.DmsProperties properties =
new com.ruoyi.train.integration.dms.config.DmsProperties();
java.lang.reflect.Field field = TrainAccessManagementController.class.getDeclaredField("dmsProperties");
field.setAccessible(true);
field.set(controller, properties);
// DMS 禁用态
properties.setEnabled(false);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> disabled =
(java.util.Map<String, Object>) controller.getCapabilities().get("data");
assertEquals(Boolean.FALSE, disabled.get("dmsEnabled"));
assertEquals(Boolean.FALSE, disabled.get("manualIssueAvailable"));
assertEquals("DMS 禁用时严禁声明外部撤权可执行", Boolean.FALSE, disabled.get("manualRevokeAvailable"));
assertEquals("本地状态治理能力必须保留", Boolean.TRUE, disabled.get("localRevokeGovernanceAvailable"));
assertEquals("LOCAL_QUEUE_ONLY", disabled.get("revokeMode"));
// DMS 启用态
properties.setEnabled(true);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> enabled =
(java.util.Map<String, Object>) controller.getCapabilities().get("data");
assertEquals(Boolean.TRUE, enabled.get("dmsEnabled"));
assertEquals(Boolean.TRUE, enabled.get("manualIssueAvailable"));
assertEquals(Boolean.TRUE, enabled.get("manualRevokeAvailable"));
assertEquals("EXTERNAL_DISPATCH", enabled.get("revokeMode"));
// 未注入配置时 fail closed
field.set(controller, null);
@SuppressWarnings("unchecked")
java.util.Map<String, Object> missing =
(java.util.Map<String, Object>) controller.getCapabilities().get("data");
assertEquals(Boolean.FALSE, missing.get("manualRevokeAvailable"));
}
}