Browse Source
- 新增 MenuType 枚举(CATALOG/MENU/BUTTON,含 fromCode 和 isLegalChild) - SysMenu 实体加 5 字段:description/perms/denyBehavior/status/apiUrl - 新增 ResourceNode DTO(含 fromEntity/toEntity 互转,映射 menuName/name、path/route) - 95 tests 全绿,零回归master
3 changed files with 152 additions and 0 deletions
@ -0,0 +1,79 @@ |
|||
package com.crm.auth.domain.dto; |
|||
|
|||
import com.crm.auth.domain.entity.SysMenu; |
|||
import com.crm.auth.domain.enums.MenuType; |
|||
import com.crm.base.utils.BeanCopyUtils; |
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serial; |
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* 权限资源树节点 DTO(ADR-0011) |
|||
* <p>管理端资源树的输入输出载体;与 SysMenu 通过 BeanCopyUtils 互转</p> |
|||
*/ |
|||
@Data |
|||
public class ResourceNode implements Serializable { |
|||
|
|||
@Serial |
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@Schema(description = "节点 ID,新增时不传、编辑时必传") |
|||
private Long id; |
|||
|
|||
@Schema(description = "父节点 ID,根节点传 null/0") |
|||
private Long parentId; |
|||
|
|||
@Schema(description = "节点名称") |
|||
private String name; |
|||
|
|||
@Schema(description = "节点类型", example = "CATALOG") |
|||
private MenuType type; |
|||
|
|||
@Schema(description = "排序", example = "1") |
|||
private Integer sort; |
|||
|
|||
@Schema(description = "描述/备注") |
|||
private String description; |
|||
|
|||
@Schema(description = "前端路由路径,menu 必填") |
|||
private String route; |
|||
|
|||
@Schema(description = "权限码(如 crm:user:list),button 必填") |
|||
private String perms; |
|||
|
|||
@Schema(description = "无权限时的前端行为:hide / disable,button 必填") |
|||
private String denyBehavior; |
|||
|
|||
@Schema(description = "接口 URL,button 必填") |
|||
private String apiUrl; |
|||
|
|||
@Schema(description = "权限点状态:enabled 启用 / disabled 停用") |
|||
private String status; |
|||
|
|||
@Schema(description = "图标 URL,catalog/menu 可选") |
|||
private String icon; |
|||
|
|||
/** SysMenu → ResourceNode(id 映射 + menuName→name + path→route) */ |
|||
public static ResourceNode fromEntity(SysMenu entity) { |
|||
ResourceNode dto = BeanCopyUtils.copy(entity, ResourceNode::new); |
|||
if (entity != null) { |
|||
dto.setName(entity.getMenuName()); |
|||
dto.setType(MenuType.fromCode(entity.getMenuType())); |
|||
dto.setRoute(entity.getPath()); |
|||
} |
|||
return dto; |
|||
} |
|||
|
|||
/** ResourceNode → SysMenu(反向映射 name→menuName + route→path) */ |
|||
public SysMenu toEntity() { |
|||
SysMenu entity = BeanCopyUtils.copy(this, SysMenu::new); |
|||
entity.setMenuName(this.name); |
|||
if (this.type != null) { |
|||
entity.setMenuType(this.type.getCode()); |
|||
} |
|||
entity.setPath(this.route); |
|||
return entity; |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
package com.crm.auth.domain.enums; |
|||
|
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
/** |
|||
* 权限资源树节点类型枚举(ADR-0011) |
|||
* <p>DB 存储 tinyint(1/2/3),代码层用枚举;提供 fromCode 工厂方法和 toCode 映射</p> |
|||
*/ |
|||
@Getter |
|||
@RequiredArgsConstructor |
|||
public enum MenuType { |
|||
|
|||
/** 菜单分组:收纳容器,不承载页面,只能挂 menu 子节点 */ |
|||
CATALOG(1, "catalog"), |
|||
|
|||
/** 菜单页面:可导航的页面,只能挂 button 子节点 */ |
|||
MENU(2, "menu"), |
|||
|
|||
/** 按钮/权限点:叶子节点,不可再挂子节点,承载权限码和接口映射 */ |
|||
BUTTON(3, "button"); |
|||
|
|||
private final int code; |
|||
private final String value; |
|||
|
|||
/** 从 DB tinyint 值反查枚举,找不到返回 null */ |
|||
public static MenuType fromCode(Integer code) { |
|||
if (code == null) return null; |
|||
return Arrays.stream(values()) |
|||
.filter(t -> t.code == code) |
|||
.findFirst() |
|||
.orElse(null); |
|||
} |
|||
|
|||
/** 是否允许作为某父类型的子节点 */ |
|||
public static boolean isLegalChild(MenuType parentType, MenuType childType) { |
|||
if (childType == null) return false; |
|||
switch (parentType) { |
|||
case CATALOG: |
|||
return childType == MENU; |
|||
case MENU: |
|||
return childType == BUTTON; |
|||
case BUTTON: |
|||
return false; // 叶子节点
|
|||
default: |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue