feat(permission): 添加菜单数据权限功能

- 新增菜单数据规则表和角色菜单数据规则关联表
- 实现菜单数据权限切面和处理器
- 添加数据规则条件和变量枚举
- 实现变量替换工具类和规则构建逻辑
- 在权限分配中集成菜单数据规则关联功能
- 优化部门ID解析逻辑,支持从用户信息中获取默认部门
- 添加菜单组件查询方法和公司访问上下文拦截器改进
This commit is contained in:
wuzongyong
2026-01-28 09:13:23 +08:00
parent b3e4055b55
commit 6c94476a8d
37 changed files with 2288 additions and 1 deletions

View File

@@ -0,0 +1,77 @@
package com.zt.plat.module.system.controller.admin.permission;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleRespVO;
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleSaveReqVO;
import com.zt.plat.module.system.convert.permission.MenuDataRuleConvert;
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
import com.zt.plat.module.system.service.permission.MenuDataRuleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
/**
* 菜单数据规则 Controller
*
* @author ZT
*/
@Tag(name = "管理后台 - 菜单数据规则")
@RestController
@RequestMapping("/system/menu-data-rule")
@Validated
public class MenuDataRuleController {
@Resource
private MenuDataRuleService menuDataRuleService;
@PostMapping("/create")
@Operation(summary = "创建菜单数据规则")
@PreAuthorize("@ss.hasPermission('system:menu:update')")
public CommonResult<Long> createMenuDataRule(@Valid @RequestBody MenuDataRuleSaveReqVO createReqVO) {
return success(menuDataRuleService.createMenuDataRule(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新菜单数据规则")
@PreAuthorize("@ss.hasPermission('system:menu:update')")
public CommonResult<Boolean> updateMenuDataRule(@Valid @RequestBody MenuDataRuleSaveReqVO updateReqVO) {
menuDataRuleService.updateMenuDataRule(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除菜单数据规则")
@Parameter(name = "id", description = "规则ID", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:menu:update')")
public CommonResult<Boolean> deleteMenuDataRule(@RequestParam("id") Long id) {
menuDataRuleService.deleteMenuDataRule(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得菜单数据规则")
@Parameter(name = "id", description = "规则ID", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system:menu:query')")
public CommonResult<MenuDataRuleRespVO> getMenuDataRule(@RequestParam("id") Long id) {
MenuDataRuleDO rule = menuDataRuleService.getMenuDataRule(id);
return success(MenuDataRuleConvert.INSTANCE.convert(rule));
}
@GetMapping("/list")
@Operation(summary = "获得菜单的所有数据规则")
@Parameter(name = "menuId", description = "菜单ID", required = true, example = "1")
@PreAuthorize("@ss.hasPermission('system:menu:query')")
public CommonResult<List<MenuDataRuleRespVO>> getMenuDataRuleList(@RequestParam("menuId") Long menuId) {
List<MenuDataRuleDO> list = menuDataRuleService.getMenuDataRuleListByMenuId(menuId);
return success(MenuDataRuleConvert.INSTANCE.convertList(list));
}
}

View File

@@ -66,6 +66,9 @@ public class PermissionController {
PermissionAssignRoleMenuItemReqVO reqVO = new PermissionAssignRoleMenuItemReqVO();
reqVO.setId(menu.getMenuId());
reqVO.setShowMenu(menu.getShowMenu());
// 获取该角色在该菜单下的数据规则ID列表
Set<Long> dataRuleIds = permissionService.getRoleMenuDataRules(roleId, menu.getMenuId());
reqVO.setDataRuleIds(dataRuleIds != null ? new ArrayList<>(dataRuleIds) : null);
return reqVO;
}).collect(Collectors.toSet());
return success(result);
@@ -83,6 +86,10 @@ public class PermissionController {
// 更新菜单的显示状态
permissionService.updateMenuDisplay(reqVO.getRoleId(), reqVO.getMenus());
// 保存菜单数据规则关联
permissionService.assignRoleMenuDataRules(reqVO.getRoleId(), reqVO.getMenus());
return success(true);
}

View File

@@ -11,6 +11,7 @@ import com.zt.plat.module.system.controller.admin.permission.vo.role.RolePageReq
import com.zt.plat.module.system.controller.admin.permission.vo.role.RoleRespVO;
import com.zt.plat.module.system.controller.admin.permission.vo.role.RoleSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.permission.RoleDO;
import com.zt.plat.framework.datapermission.core.menudatapermission.annotation.PermissionData;
import com.zt.plat.module.system.service.permission.RoleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -78,6 +79,7 @@ public class RoleController {
@GetMapping("/page")
@Operation(summary = "获得角色分页")
@PreAuthorize("@ss.hasPermission('system:role:query')")
@PermissionData(pageComponent = "system/role/index")
public CommonResult<PageResult<RoleRespVO>> getRolePage(RolePageReqVO pageReqVO) {
PageResult<RoleDO> pageResult = roleService.getRolePage(pageReqVO);
// 获取所有父级角色信息

View File

@@ -0,0 +1,46 @@
package com.zt.plat.module.system.controller.admin.permission.vo.menudatarule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 菜单数据规则 Response VO
*
* @author ZT
*/
@Schema(description = "管理后台 - 菜单数据规则 Response VO")
@Data
public class MenuDataRuleRespVO {
@Schema(description = "规则ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "菜单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long menuId;
@Schema(description = "规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "仅看本部门数据")
private String ruleName;
@Schema(description = "规则字段", example = "dept_id")
private String ruleColumn;
@Schema(description = "规则条件", requiredMode = Schema.RequiredMode.REQUIRED, example = "=")
private String ruleConditions;
@Schema(description = "规则值", requiredMode = Schema.RequiredMode.REQUIRED, example = "#{deptId}")
private String ruleValue;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "排序", example = "1")
private Integer sort;
@Schema(description = "备注", example = "限制只能查看本部门数据")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,53 @@
package com.zt.plat.module.system.controller.admin.permission.vo.menudatarule;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
* 菜单数据规则创建/修改 Request VO
*
* @author ZT
*/
@Schema(description = "管理后台 - 菜单数据规则创建/修改 Request VO")
@Data
public class MenuDataRuleSaveReqVO {
@Schema(description = "规则ID", example = "1024")
private Long id;
@Schema(description = "菜单ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "菜单ID不能为空")
private Long menuId;
@Schema(description = "规则名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "仅看本部门数据")
@NotBlank(message = "规则名称不能为空")
@Size(max = 100, message = "规则名称长度不能超过 100 个字符")
private String ruleName;
@Schema(description = "规则字段", example = "dept_id")
@Size(max = 100, message = "规则字段长度不能超过 100 个字符")
private String ruleColumn;
@Schema(description = "规则条件", requiredMode = Schema.RequiredMode.REQUIRED, example = "=")
@NotBlank(message = "规则条件不能为空")
@Size(max = 20, message = "规则条件长度不能超过 20 个字符")
private String ruleConditions;
@Schema(description = "规则值", requiredMode = Schema.RequiredMode.REQUIRED, example = "#{deptId}")
@NotBlank(message = "规则值不能为空")
@Size(max = 500, message = "规则值长度不能超过 500 个字符")
private String ruleValue;
@Schema(description = "状态", example = "1")
private Integer status;
@Schema(description = "排序", example = "1")
private Integer sort;
@Schema(description = "备注", example = "限制只能查看本部门数据")
@Size(max = 500, message = "备注长度不能超过 500 个字符")
private String remark;
}

View File

@@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 赋予角色菜单--菜单列表 Request VO")
@Data
@@ -19,4 +21,7 @@ public class PermissionAssignRoleMenuItemReqVO {
@Schema(description = "是否显示菜单按钮是否点击过(避免大量更新数据,只更新点击过的)")
private Boolean showMenuChanged = false;
@Schema(description = "菜单数据规则ID列表", example = "[1, 2, 3]")
private List<Long> dataRuleIds;
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.system.convert.permission;
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleRespVO;
import com.zt.plat.module.system.controller.admin.permission.vo.menudatarule.MenuDataRuleSaveReqVO;
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 菜单数据规则 Convert
*
* @author ZT
*/
@Mapper
public interface MenuDataRuleConvert {
MenuDataRuleConvert INSTANCE = Mappers.getMapper(MenuDataRuleConvert.class);
MenuDataRuleDO convert(MenuDataRuleSaveReqVO bean);
MenuDataRuleRespVO convert(MenuDataRuleDO bean);
List<MenuDataRuleRespVO> convertList(List<MenuDataRuleDO> list);
}

View File

@@ -0,0 +1,67 @@
package com.zt.plat.module.system.dal.dataobject.permission;
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 菜单数据规则 DO
*
* @author ZT
*/
@TableName("system_menu_data_rule")
@KeySequence("system_menu_data_rule_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class MenuDataRuleDO extends TenantBaseDO {
/**
* 规则ID
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 菜单ID
*/
private Long menuId;
/**
* 规则名称
*/
private String ruleName;
/**
* 规则字段(数据库列名)
*/
private String ruleColumn;
/**
* 规则条件(=、>、<、IN、LIKE等
*/
private String ruleConditions;
/**
* 规则值(支持变量如#{userId}、#{deptId}
*/
private String ruleValue;
/**
* 状态0=禁用 1=启用)
*/
private Integer status;
/**
* 排序
*/
private Integer sort;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,42 @@
package com.zt.plat.module.system.dal.dataobject.permission;
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 角色菜单数据规则关联 DO
*
* @author ZT
*/
@TableName("system_role_menu_data_rule")
@KeySequence("system_role_menu_data_rule_seq")
@Data
@EqualsAndHashCode(callSuper = true)
public class RoleMenuDataRuleDO extends TenantBaseDO {
/**
* 自增主键
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 角色ID
*/
private Long roleId;
/**
* 菜单ID
*/
private Long menuId;
/**
* 数据规则ID
*/
private Long dataRuleId;
}

View File

@@ -0,0 +1,59 @@
package com.zt.plat.module.system.dal.mysql.permission;
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
import com.zt.plat.module.system.dal.dataobject.permission.MenuDataRuleDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Collection;
import java.util.List;
/**
* 菜单数据规则 Mapper
*
* @author ZT
*/
@Mapper
public interface MenuDataRuleMapper extends BaseMapperX<MenuDataRuleDO> {
/**
* 根据菜单ID查询规则列表
*
* @param menuId 菜单ID
* @return 规则列表
*/
default List<MenuDataRuleDO> selectListByMenuId(Long menuId) {
return selectList(MenuDataRuleDO::getMenuId, menuId);
}
/**
* 根据角色和菜单查询规则ID列表
*
* @param roleIds 角色ID集合
* @param menuId 菜单ID
* @return 规则ID列表
*/
@Select("<script>" +
"SELECT DISTINCT rmdr.data_rule_id " +
"FROM system_role_menu_data_rule rmdr " +
"WHERE rmdr.role_id IN " +
"<foreach collection='roleIds' item='roleId' open='(' separator=',' close=')'>" +
"#{roleId}" +
"</foreach>" +
"AND rmdr.menu_id = #{menuId} " +
"AND rmdr.deleted = 0" +
"</script>")
List<Long> selectRuleIdsByRoleAndMenu(@Param("roleIds") Collection<Long> roleIds,
@Param("menuId") Long menuId);
/**
* 批量查询菜单的规则
*
* @param menuIds 菜单ID集合
* @return 规则列表
*/
default List<MenuDataRuleDO> selectListByMenuIds(Collection<Long> menuIds) {
return selectList("menu_id", menuIds);
}
}

Some files were not shown because too many files have changed in this diff Show More