DataScopeAspect
数据过滤处理
此切面在执行带有@ControllerDataScope
注解的方法之前进行数据权限过滤。首先获取当前登录用户,然后判断当前用户是否为超级管理员。如果不是超级管理员,则获取权限字符,默认使用上下文中的权限字符。接下来,根据用户的角色和数据范围类型进行数据权限过滤。根据不同的数据范围类型,生成相应的SQL条件,并将其加入到参数中。最后,在拼接权限SQL之前,先清空参数中的params.dataScope
参数,以防止注入。
注意:以上代码中涉及的StringUtils
、SysUser
、SysRole
、SysDept
等类和方法是示例中的自定义类和方法,实际使用时需要根据项目的具体情况进行替换或实现。
@RestController
: 表示这是一个控制器类,并且处理的请求返回的是 JSON 数据。@Resource(name = "captchaProducer")
和@Resource(name = "captchaProducerMath")
: 注入名为 "captchaProducer" 和 "captchaProducerMath" 的Producer
对象。这里使用了@Resource
注解,表示通过名称来注入对应的 Bean。@Autowired
: 注入RedisCache
和ISysConfigService
对象。@GetMapping("/captchaImage")
: 处理 GET 请求路径为 "/captchaImage" 的请求。AjaxResult
: 一个自定义的结果封装类,用于返回响应结果。
用于表示路由的元数据。包括路由的标题(title
)、图标(icon
)、是否缓存(noCache
)和内链地址(link
)。
@Service
: 将类声明为一个服务类,由Spring进行管理。@Autowired
: 自动装配依赖关系,将SysConfigMapper
和RedisCache
注入到当前类中。@PostConstruct
: 在构造函数执行完成后,执行init
方法。用于在项目启动时初始化参数到缓存。@Override
: 表示重写或实现了父类或接口的方法。@DataSource(DataSourceType.MASTER)
: 指定数据源为主数据源,用于数据库访问。config.setConfigId(configId)
: 设置SysConfig
对象的configId
属性值为configId
。return configMapper.selectConfig(config)
: 调用configMapper
的selectConfig
方法查询参数配置信息并返回。String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey)))
: 从缓存中获取参数键对应的参数值。if (StringUtils.isNotEmpty(configValue)) { return configValue; }
: 如果参数值不为空,则直接返回参数值。config.setConfigKey(configKey)
: 设置SysConfig
对象的configKey
属性值为configKey
。SysConfig retConfig = configMapper.selectConfig(config)
: 调用configMapper
的selectConfig
方法查询参数配置信息并赋值给retConfig
。if (StringUtils.isNotNull(retConfig)) { redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); return retConfig.getConfigValue(); }
: 如果查询到参数配置信息,将其存入缓存,并返回参数值。return StringUtils.EMPTY;
: 返回空字符串。String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled")
: 调用selectConfigByKey
方法获取验证码开关的参数值。if (StringUtils.isEmpty(captchaEnabled)) { return true; }
: 如果参数值为空,则返回true
,表示开启验证码。return Convert.toBool(captchaEnabled)
: 将参数值转为布尔型并返回。return configMapper.selectConfigList(config)
: 调用configMapper
的selectConfigList
方法查询参数配置列表并返回。int row = configMapper.insertConfig(config)
: 调用configMapper
的insertConfig
方法插入参数配置信息并将受影响的行数赋值给row
。if (row > 0) { redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); }
: 如果插入成功,则将参数配置信息存入缓存。SysConfig temp = configMapper.selectConfigById(config.getConfigId())
: 调用configMapper
的selectConfigById
方法查询旧的参数配置信息并赋值给temp
。if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) { redisCache.deleteObject(getCacheKey(temp.getConfigKey())); }
: 如果参数键发生变化,则从缓存中删除旧的参数配置信息。int row = configMapper.updateConfig(config)
: 调用configMapper
的updateConfig
方法更新参数配置信息并将受影响的行数赋值给row
。if (row > 0) { redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); }
: 如果更新成功,则将新的参数配置信息存入缓存。for (Long configId : configIds) { SysConfig config = selectConfigById(configId); ... }
: 遍历要删除的参数ID,调用selectConfigById
方法查询参数配置信息并判断是否允许删除内置参数,然后调用deleteConfigById
方法删除参数配置信息,并从缓存中删除。List<SysConfig> configsList = configMapper.selectConfigList(new SysConfig())
: 调用configMapper
的selectConfigList
方法查询参数配置列表并赋值给configsList
。redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue())
: 将参数配置信息存入缓存。Collection<String> keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*")
: 获取所有参数配置的缓存键。redisCache.deleteObject(keys)
: 清空缓存。clearConfigCache()
: 清空参数缓存数据。loadingConfigCache()
: 加载参数缓存数据。Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId()
: 获取参数配置ID,如果为空则设置为-1。SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey())
: 调用configMapper
的checkConfigKeyUnique
方法查询是否存在相同的参数键名。if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) { return UserConstants.NOT_UNIQUE; }
: 如果存在相同的参数键名且不是当前配置的参数键名,则返回false
。return UserConstants.UNIQUE;
: 参数键名唯一,返回true
。return CacheConstants.SYS_CONFIG_KEY + configKey
: 返回缓存键。private String getCacheKey(String configKey)
: 私有方法,用于获取缓存键。
roleMapper.selectRolePermissionByUserId(userId)
查询角色权限列表。然后,创建一个空的权限集合permsSet
。接下来,遍历角色权限列表,对每个角色进行处理。如果角色对象perm
不为空,则将其角色权限的roleKey
字段按逗号分隔后转换为列表,并将列表中的权限添加到permsSet
集合中。最后,返回权限集合permsSet
,该集合包含了用户拥有的所有角色权限。
ISysConfigService
ISysMenuService
定义完ISysDictDataService
接口后,您需要创建一个实现类来继承该接口。在Java中,接口定义了一组方法的契约或规范,但它们并不提供这些方法的实际实现。实现类负责为接口中声明的每个方法提供具体的实现。
如果不编写实现类,那么ISysDictDataService
接口将仅仅是一个接口的定义,没有具体的实现逻辑。在这种情况下,其他部分的代码将无法直接调用接口中定义的方法,因为没有实现提供实际的方法实现。
接口本身是一种规范或契约,它定义了一组方法的签名,用于指导实现类的开发。实现类负责提供方法的实际逻辑和功能。
如果您不编写实现类,那么接口只是一个接口声明,它可能会在代码中的其他地方作为类型或约定使用,但不能直接调用其方法。
通常情况下,我们会编写实现类来实现接口中定义的方法,以便在需要时可以实际调用这些方法并执行相应的逻辑。如果没有实现类,接口的定义将没有具体的实际用途。
因此,为了能够使用接口中定义的方法,您需要编写一个实现类来提供具体的方法实现。这样,其他部分的代码可以通过实例化实现类并调用接口中的方法来使用这些功能
在Java中,接口(interface)是一种抽象类型,它定义了一组方法的签名但没有具体的实现。当您编写完ISysDictDataService
接口后,并不需要强制编写一个实现类去继承它。
编写实现类的目的是为了提供接口中定义的方法的具体实现逻辑。如果不编写实现类,编译过程不会报错,因为接口本身不需要被实例化或直接调用。
然而,如果其他部分的代码(如其他类或方法)试图调用ISysDictDataService
接口中的方法,而没有相应的实现类提供具体实现,那么在运行时会抛出java.lang.UnsupportedOperationException
异常。
因此,虽然不是强制要求编写实现类,但通常情况下,为了能够使用接口中定义的方法
@Override
:注解表示该方法是对父类或接口的重写。public boolean checkRoleNameUnique(SysRole role)
:方法签名,指定了该方法的访问修饰符、返回类型和参数。Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId();
:使用三元运算符判断角色ID是否为空,如果为空,则将roleId
赋值为-1,否则为角色的实际ID。SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName());
:调用roleMapper
对象的checkRoleNameUnique
方法,根据角色名查找角色信息,并将结果赋值给info
变量。if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue())
:判断info
对象不为空且角色ID与roleId
不相等。StringUtils.isNotNull(info)
:调用StringUtils
类的isNotNull
方法判断info
是否不为空。info.getRoleId().longValue() != roleId.longValue()
:将info
对象的角色ID与roleId
进行比较,判断是否相等。
return UserConstants.NOT_UNIQUE;
:如果角色名不唯一,则返回UserConstants.NOT_UNIQUE
,表示角色名不唯一。return UserConstants.UNIQUE;
:如果角色名唯一,则返回UserConstants.UNIQUE
,表示角色名唯一。
该方法用于检查角色键值是否唯一。
@Override
:注解表示该方法是对父类或接口的重写。public boolean checkRoleKeyUnique(SysRole role)
:方法签名,指定了该方法的访问修饰符、返回类型和参数。Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId();
:使用三元运算符判断角色ID是否为空,如果为空,则将roleId
赋值为-1,否则为角色的实际ID。SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey());
:调用roleMapper
对象的checkRoleKeyUnique
方法,根据角色键值查找角色信息,并将结果赋值给info
变量。if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue())
:判断info
对象不为空且角色ID与roleId
不相等。StringUtils.isNotNull(info)
:调用StringUtils
类的isNotNull
方法判断info
是否不为空。info.getRoleId().longValue() != roleId.longValue()
:将info
对象的角色ID与roleId
进行比较,判断是否相等。
return UserConstants.NOT_UNIQUE;
:如果角色键值不唯一,则返回UserConstants.NOT_UNIQUE
,表示角色键值不唯一。return UserConstants.UNIQUE;
:如果角色键值唯一,则返回UserConstants.UNIQUE
,表示角色键值唯一。@Override
:注解表示该方法是对父类或接口的重写。public void checkRoleAllowed(SysRole role)
:方法签名,指定了该方法的访问修饰符、返回类型和参数。if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin())
:判断角色的ID不为空且角色为超级管理员。StringUtils.isNotNull(role.getRoleId())
:调用StringUtils
类的isNotNull
方法判断角色的ID是否不为空。role.isAdmin()
:调用role
对象的isAdmin
方法判断角色是否为超级管理员。
throw new ServiceException("不允许操作超级管理员角色");
:抛出ServiceException
异常,提示不允许操作超级管理员角色。@Service
: 标识这是一个服务类,用于处理业务逻辑。public class SysMenuServiceImpl implements ISysMenuService
: 定义了一个名为SysMenuServiceImpl
的类,该类实现了ISysMenuService
接口。public static final String PREMISSION_STRING = "perms["{0}"]";
: 声明了一个名为PREMISSION_STRING
的常量,用于定义权限字符串的格式。@Autowired
: 自动注入依赖对象。private SysMenuMapper menuMapper;
: 声明了一个私有字段menuMapper
,用于访问菜单数据的对象。private SysRoleMapper roleMapper;
: 声明了一个私有字段roleMapper
,用于访问角色数据的对象。private SysRoleMenuMapper roleMenuMapper;
: 声明了一个私有字段roleMenuMapper
,用于访问角色菜单关系数据的对象。@Override
: 表示该方法是对父类或接口中同名方法的重写。public List<SysMenu> selectMenuList(Long userId)
: 声明了一个公共方法selectMenuList
,接受一个Long
类型的参数userId
,返回一个SysMenu
类型的列表。return selectMenuList(new SysMenu(), userId);
: 调用另一个名为selectMenuList
的方法,并传递一个新创建的SysMenu
对象和userId
参数作为参数。@Override
: 表示该方法是对父类或接口中同名方法的重写。public List<SysMenu> selectMenuList(SysMenu menu, Long userId)
: 声明了一个公共方法selectMenuList
,接受一个SysMenu
类型的参数menu
和一个Long
类型的参数userId
,返回一个SysMenu
类型的列表。List<SysMenu> menuList = null;
: 声明了一个名为menuList
的变量,类型为List<SysMenu>
,初始值为null
。if (SysUser.isAdmin(userId))
: 判断当前用户是否是管理员。menuList = menuMapper.selectMenuList(menu);
: 如果当前用户是管理员,则调用menuMapper
对象的selectMenuList
方法来获取所有菜单信息。menu.getParams().put("userId", userId);
: 如果当前用户不是管理员,则将用户ID存储到menu
对象的参数中。menuList = menuMapper.selectMenuListByUserId(menu);
: 调用menuMapper
对象的selectMenuListByUserId
方法来根据用户ID获取菜单列表。return menuList;
: 返回菜单列表。@Override
: 表示该方法是对父类或接口中同名方法的重写。public List<Long> selectMenuListByRoleId(Long roleId)
: 声明了一个公共方法selectMenuListByRoleId
,接受一个Long
类型的参数roleId
,返回一个List<Long>
类型的结果。SysRole role = roleMapper.selectRoleById(roleId);
: 调用roleMapper
对象的selectRoleById
方法,根据角色ID获取对应的角色信息,并将结果存储在role
变量中。return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly());
: 调用menuMapper
对象的selectMenuListByRoleId
方法,根据角色ID和角色的菜单严格检查属性获取对应的菜单列表,并作为方法的返回值。@Override
: 表示该方法是对父类或接口中同名方法的重写。public List<RouterVo> buildMenus(List<SysMenu> menus)
: 声明了一个公共方法buildMenus
,接受一个List<SysMenu>
类型的参数menus
,返回一个List<RouterVo>
类型的结果。List<RouterVo> routers = new LinkedList<RouterVo>();
: 声明了一个名为routers
的变量,类型为List<RouterVo>
,使用LinkedList
初始化。for (SysMenu menu : menus) { ... }
: 遍历menus
列表中的每个SysMenu
对象,使用变量menu
进行迭代。RouterVo router = new RouterVo();
: 创建一个新的RouterVo
对象,用于存储路由信息。router.setHidden("1".equals(menu.getVisible()));
: 根据menu
对象的visible
属性设置路由是否隐藏。router.setName(getRouteName(menu));
: 调用getRouteName
方法获取路由名称,并设置到router
对象中。router.setPath(getRouterPath(menu));
: 调用getRouterPath
方法获取路由路径,并设置到router
对象中。router.setComponent(getComponent(menu));
: 调用getComponent
方法获取路由组件,并设置到router
对象中。router.setQuery(menu.getQuery());
: 设置路由的查询参数为menu
对象的query
属性。router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
: 创建一个新的MetaVo
对象,设置其属性值为menu
对象的相关属性,并将其设置为router
对象的元数据。List<SysMenu> cMenus = menu.getChildren();
: 获取当前菜单的子菜单列表。if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) { ... }
: 如果子菜单列表不为空且当前菜单类型为目录类型。router.setAlwaysShow(true);
: 设置路由是否总是显示为true
。router.setRedirect("noRedirect");
: 设置路由的重定向为"noRedirect"
。router.setChildren(buildMenus(cMenus));
: 递归调用buildMenus
方法构建子菜单的路由,并将结果设置为当前路由的子路由。else if (isMenuFrame(menu)) { ... }
: 否则,如果当前菜单为菜单框架。router.setMeta(null);
: 清空路由的元数据。List<RouterVo> childrenList = new ArrayList<RouterVo>();
: 声明一个新的ArrayList<RouterVo>
对象,用于存储子路由列表。RouterVo children = new RouterVo();
: 创建一个新的RouterVo
对象,用于存储子路由信息。children.setPath(menu.getPath());
: 设置子路由的路径为当前菜单的路径。children.setComponent(menu.getComponent());
: 设置子路由的组件为当前菜单的组件。children.setName(StringUtils.capitalize(menu.getPath()));
: 设置子路由的名称为当前菜单路径的首字母大写形式。children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath()));
: 创建一个新的MetaVo
对象,设置其属性值为当前菜单的相关属性,并将其设置为子路由的元数据。children.setQuery(menu.getQuery());
: 设置子路由的查询参数为当前菜单的查询参数。childrenList.add(children);
: 将子路由添加到子路由列表中。router.setChildren(childrenList);
: 设置子路由列表为当前路由的子路由列表。else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) { ... }
: 否则,如果当前菜单的父菜单ID为0且是内部链接。router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon()));
: 创建一个新的MetaVo
对象,设置其属性值为当前菜单的名称和图标,并将其设置为当前路由的元数据。router.setPath("/");
: 设置当前路由的路径为根路径。List<RouterVo> childrenList = new ArrayList<RouterVo>();
: 声明一个新的ArrayList<RouterVo>
对象,用于存储子路由列表。RouterVo children = new RouterVo();
: 创建一个新的RouterVo
对象,用于存储子路由信息。String routerPath = innerLinkReplaceEach(menu.getPath());
: 调用innerLinkReplaceEach
方法替换内部链接的路径。children.setPath(routerPath);
: 设置子路由的路径为替换后的路径。children.setComponent(UserConstants.INNER_LINK);
: 设置子路由的组件为内部链接组件。children.setName(StringUtils.capitalize(routerPath));
: 设置子路由的名称为替换后的路径的首字母大写形式。children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath()));
: 创建一个新的MetaVo
对象,设置其属性值为当前菜单的相关属性,并将其设置为子路由的元数据。childrenList.add(children);
: 将子路由添加到子路由列表中。router.setChildren(childrenList);
: 设置子路由列表为当前路由的子路由列表。routers.add(router);
: 将当前路由添加到路由列表中。return routers;
: 返回路由列表作为方法的结果。Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
:获取菜单ID,若菜单ID为空,则将其设为-1。SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId());
:调用菜单Mapper的checkMenuNameUnique
方法,查询具有相同菜单名称和父菜单ID的菜单信息。menu.getMenuName()
:获取菜单的名称。menu.getParentId()
:获取菜单的父菜单ID。
if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) {...}
:判断查询到的菜单信息是否存在且菜单ID与当前菜单ID不相同。StringUtils.isNotNull(info)
:判断菜单信息是否不为空。info.getMenuId().longValue() != menuId.longValue()
:判断查询到的菜单ID与当前菜单ID是否不相同。
return UserConstants.NOT_UNIQUE;
:菜单名称不唯一,返回不唯一标识。return UserConstants.UNIQUE;
:菜单名称唯一,返回唯一标识。@Service
:用于标识该类为服务类,由Spring进行管理和注入。public class SysConfigServiceImpl implements ISysConfigService
:定义了类SysConfigServiceImpl
,实现了接口ISysConfigService
。@Autowired
:自动注入依赖的对象。private SysConfigMapper configMapper;
:声明一个私有成员变量configMapper
,用于访问SysConfigMapper
对象。private RedisCache redisCache;
:声明一个私有成员变量redisCache
,用于访问RedisCache
对象。@PostConstruct
:在对象创建后,调用init
方法进行初始化操作。public void init()
:初始化方法,用于在项目启动时将参数加载到缓存中。loadingConfigCache()
:自定义的方法,用于加载参数到缓存中。@Override
:表示该方法覆盖了父类或接口的同名方法。@DataSource(DataSourceType.MASTER)
:指定数据源为主数据库,用于读取配置信息。public SysConfig selectConfigById(Long configId)
:方法签名,指定了该方法的访问修饰符、返回类型和参数。SysConfig config = new SysConfig();
:创建一个SysConfig
对象,用于设置查询条件。config.setConfigId(configId);
:将传入的configId
设置为config
对象的configId
属性值。return configMapper.selectConfig(config);
:调用configMapper
的selectConfig
方法,传入config
对象作为参数,查询并返回匹配的配置信息对象。
该方法将给定的配置对象 config
插入到数据库中,使用 configMapper
的 insertConfig
方法执行插入操作,并返回受影响的行数。
如果插入成功(受影响的行数大于0),则将配置信息存储到 Redis 缓存中,使用 redisCache
的 setCacheObject
方法。
最后,返回插入结果(受影响的行数)。
注解中的 @param
表示方法的参数,@return
表示方法的返回值。
加群联系作者vx:xiaoda0423
仓库地址:https://github.com/webVueBlog/JavaGuideInterview