task-275(验收反馈): 菜单权限标识/路由后台自动生成(menu_auto_/auto/ 前缀),表单移除两项输入;编辑缺省保持原值

This commit is contained in:
2026-09-06 01:57:17 +08:00
parent c3dc2a3f2f
commit 6e2e4bf8e7
10 changed files with 152 additions and 34 deletions
@@ -10,7 +10,7 @@ public class PermissionMenuCreateRequest {
@NotBlank(message = "菜单名称不能为空")
private String name;
@NotBlank(message = "菜单标识不能为空")
/** 菜单标识;不传由后台自动生成(menu_auto_<类型>_<时间戳>)。 */
@JsonAlias("column_key")
private String columnKey;
@@ -22,7 +22,7 @@ public class PermissionMenuCreateRequest {
@JsonAlias("menu_type")
private String menuType;
@NotBlank(message = "菜单路由不能为空")
/** 页面路由;不传由后台自动生成(auto/<菜单标识>)。 */
@JsonAlias("route_path")
private String routePath;
@@ -10,7 +10,7 @@ public class PermissionMenuUpdateRequest {
@NotBlank(message = "菜单名称不能为空")
private String name;
@NotBlank(message = "菜单标识不能为空")
/** 菜单标识;不传保持原值(表单不采集)。 */
@JsonAlias("column_key")
private String columnKey;
@@ -22,7 +22,7 @@ public class PermissionMenuUpdateRequest {
@JsonAlias("menu_type")
private String menuType;
@NotBlank(message = "菜单路由不能为空")
/** 页面路由;不传保持原值(表单不采集)。 */
@JsonAlias("route_path")
private String routePath;
@@ -74,9 +74,16 @@ public class PermissionMenuService {
throw new BusinessException("菜单参数不能为空");
}
String name = normalizeRequired(request.getName(), "菜单名称不能为空");
String columnKey = normalizeRequired(request.getColumnKey(), "菜单标识不能为空");
String menuType = normalizeMenuTypeRequired(request.getMenuType());
String routePath = normalizeRequired(request.getRoutePath(), "页面路径不能为空");
// 权限标识/路由由后台自动生成:表单不采集,缺省时生成稳定唯一值。
String columnKey = normalizeOptional(request.getColumnKey());
if (columnKey == null) {
columnKey = generateColumnKey(menuType);
}
String routePath = normalizeOptional(request.getRoutePath());
if (routePath == null) {
routePath = generateRoutePath(columnKey);
}
Long parentId = normalizeParentId(request.getParentId());
ensureParentValid(parentId, null, menuType);
ensureUniqueColumnKey(columnKey, null);
@@ -107,9 +114,16 @@ public class PermissionMenuService {
throw new BusinessException("菜单参数不能为空");
}
String name = normalizeRequired(request.getName(), "菜单名称不能为空");
String columnKey = normalizeRequired(request.getColumnKey(), "菜单标识不能为空");
String menuType = normalizeMenuTypeRequired(request.getMenuType());
String routePath = normalizeRequired(request.getRoutePath(), "页面路径不能为空");
// 编辑时未传权限标识/路由则保持原值(表单不采集,避免覆盖为自动值)。
String columnKey = normalizeOptional(request.getColumnKey());
if (columnKey == null) {
columnKey = entity.getColumnKey();
}
String routePath = normalizeOptional(request.getRoutePath());
if (routePath == null) {
routePath = entity.getRoutePath();
}
Long parentId = normalizeParentId(request.getParentId());
ensureParentValid(parentId, id, menuType);
ensureMenuTypeChangeAllowed(entity, menuType);
@@ -824,6 +838,26 @@ public class PermissionMenuService {
return normalized;
}
/** trim 后为空的返回 null(用于“后台自动生成/保持原值”的可选字段)。 */
private String normalizeOptional(String value) {
String normalized = value == null ? "" : value.trim();
return normalized.isEmpty() ? null : normalized;
}
/** 未传菜单标识时后台自动生成(menu_auto_<类型>_<毫秒时间戳>,唯一性由 ensureUniqueColumnKey 兜底)。 */
private String generateColumnKey(String menuType) {
String generated = "menu_auto_" + menuType + "_" + System.currentTimeMillis();
log.info("[menu-auto-key] 未传菜单标识,后台自动生成 columnKey={}", generated);
return generated;
}
/** 未传页面路径时后台自动生成(auto/<菜单标识>,不与真实页面路由冲突)。 */
private String generateRoutePath(String columnKey) {
String generated = "auto/" + columnKey;
log.info("[menu-auto-key] 未传页面路径,后台自动生成 routePath={}", generated);
return generated;
}
private Long normalizeParentId(Long value) {
return value == null || value <= 0 ? null : value;
}
@@ -672,6 +672,58 @@ class PermissionMenuServiceTest {
assertThat(inserted.getValue().getMenuType()).isEqualTo("admin");
}
@Test
void createWithoutColumnKeyAndRoutePathAutoGenerates() {
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
PermissionMenuEntity adminTail = menu(9L, null, "admin", 134);
adminTail.setName("admin tail");
when(menuMapper.selectOne(any())).thenReturn(null, null, adminTail);
when(menuMapper.selectById(any())).thenReturn(adminTail);
PermissionMenuCreateRequest request = new PermissionMenuCreateRequest();
request.setName("新菜单");
request.setMenuType("admin");
// 不传 columnKey / routePath:由后台自动生成。
service.create(request);
ArgumentCaptor<PermissionMenuEntity> inserted = ArgumentCaptor.forClass(PermissionMenuEntity.class);
verify(menuMapper).insert(inserted.capture());
assertThat(inserted.getValue().getColumnKey()).startsWith("menu_auto_admin_");
assertThat(inserted.getValue().getRoutePath()).startsWith("auto/").contains(inserted.getValue().getColumnKey());
}
@Test
void updateWithoutColumnKeyAndRoutePathKeepsOriginal() {
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
PermissionMenuEntity current = menu(1L, null, "admin", 1);
current.setName("原名");
when(menuMapper.selectById(1L)).thenReturn(current);
when(menuMapper.selectOne(any())).thenReturn(null, null, current);
when(menuMapper.selectById(any())).thenReturn(current);
PermissionMenuUpdateRequest request = new PermissionMenuUpdateRequest();
request.setName("改名");
request.setMenuType("admin");
// 不传 columnKey / routePath:保持原值。
service.update(1L, request);
ArgumentCaptor<PermissionMenuEntity> updated = ArgumentCaptor.forClass(PermissionMenuEntity.class);
verify(menuMapper).updateById(updated.capture());
assertThat(updated.getValue().getColumnKey()).isEqualTo("menu-1");
assertThat(updated.getValue().getRoutePath()).isEqualTo("route-1");
assertThat(updated.getValue().getName()).isEqualTo("改名");
}
private PermissionMenuCreateRequest createRequest(Long parentId, String menuType) {
PermissionMenuCreateRequest request = new PermissionMenuCreateRequest();
request.setName("child");