个人随笔
目录
若依框架的简单学习
2024-12-09 22:07:50

若依框架其实算是低代码开发框架,如果只是做一些简单的增删查改需求,那么基本上不需要写代码,复杂一点的需求也可以生成增删改查后进行简单的修改就可以了。今天再这里简单的学习一下下。

一、下载安装

1、下载源码

我这里做的是前后端分离的版本,去到官网
https://doc.ruoyi.vip/ruoyi-vue/
https://gitee.com/y_project/RuoYi-Vue
用git或者直接下载代码,这个就是后端代码。

里面有个前端,但是我们前端不用这个,因为这个是vue2版本的,我们从gitee的remark可以看到vue3版本的地址。

https://github.com/yangzongzhuan/RuoYi-Vue3

2、后台启动

我们需要redis和mysql,这里准备好后就修改相应的配置启动。对于mysql我们需要先建库

  1. create database ry_vue;

然后执行sql下面的两个文件。配置修改地方如下


然后启动ruoyi-admin即可,后台启动后,端口是8080,所有接口都是

  1. http://localhost:8080

3、前端启动

前端下载完后,执行

  1. npm install
  2. npm run dev

就可以用了,如果依赖下载慢可能要切换镜像https://registry.npmmirror.com。

4、访问测试

前后端启动后就可以访问了,用户密码admin/admin123

二、低代码开发

假设我们有个读物的维护功能,要有增删改导出等功能,那么怎么快速搞定呢?这里就要借助低代码开发了。假设有这样一张表.

1、建表

  1. CREATE TABLE `reading` (
  2. `id` BIGINT(20) NOT NULL COMMENT '主键ID,雪花算法生成',
  3. `chinese_name` VARCHAR(255) NULL DEFAULT NULL COMMENT '中文名称' COLLATE 'utf8mb4_unicode_ci',
  4. `english_name` VARCHAR(255) NULL DEFAULT NULL COMMENT '英文名称' COLLATE 'utf8mb4_unicode_ci',
  5. `image_url` VARCHAR(255) NULL DEFAULT NULL COMMENT '书本图片' COLLATE 'utf8mb4_unicode_ci',
  6. `author_name` VARCHAR(100) NULL DEFAULT NULL COMMENT '作者名称' COLLATE 'utf8mb4_unicode_ci',
  7. `auth_desc` VARCHAR(1024) NULL DEFAULT NULL COMMENT '作者简介' COLLATE 'utf8mb4_unicode_ci',
  8. `content_desc` VARCHAR(1024) NULL DEFAULT NULL COMMENT '内容简介' COLLATE 'utf8mb4_unicode_ci',
  9. `status` CHAR(1) NULL DEFAULT NULL COMMENT '状态:0已下架,1已上架,2查看广告' COLLATE 'utf8mb4_unicode_ci',
  10. `create_datetime` VARCHAR(32) NULL DEFAULT NULL COMMENT '创建时间' COLLATE 'utf8mb4_unicode_ci',
  11. `sort` BIGINT(20) NULL DEFAULT NULL COMMENT '排序',
  12. `type` CHAR(1) NULL DEFAULT '0' COMMENT '类型,0是默认,1是首页推荐,2精品,3-最新更新' COLLATE 'utf8mb4_unicode_ci',
  13. `reading_status` CHAR(1) NULL DEFAULT '0' COMMENT '类型,0是更新中,1是已完本' COLLATE 'utf8mb4_unicode_ci',
  14. `reading_type` CHAR(1) NULL DEFAULT '0' COMMENT '类型,0拓展阅读,1小学初中,2高中大学' COLLATE 'utf8mb4_unicode_ci',
  15. PRIMARY KEY (`id`) USING BTREE
  16. )
  17. COLLATE='utf8mb4_unicode_ci'
  18. ENGINE=InnoDB
  19. ;

2、通过导入功能导入表

3、编辑表



这里主要的是字段信息
【插入】值得是新增功能要维护的字段
【编辑】表示更新的时候要维护的字段
【列表】表示列表要展示的字段
【查询】表示要作为查询条件的字段

这里生成后后续我们只需要对代码进行小修改就好了,比如ID,我们会改下代码按我们自己的规范来生成比如

  1. public int insertReading(Reading reading)
  2. {
  3. reading.setId(System.currentTimeMillis());
  4. reading.setCreateDatetime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
  5. return readingMapper.insertReading(reading);
  6. }

还有下拉框的类型,这里我们是从字典那里配置好

4、生成代码

生成完后是个压缩包,里面有后端的代码和前端的代码,以及相关的sql文件。我们只需要执行完sql将对应的代码黏贴到对应的目录重启就可以了。


启动就可以看到。

当然导出也是OK的,不过上面的截图我是调整过后的代码。

三、权限管理逻辑

在角色管理里面有个数据权限,发现权限功能很强大,那若依是怎么做到的呢?通过调试可以发现打印的ssql后面拼上了

  1. and (dept_id =103)

然后去看代码Mapper的代码

多了

  1. ${params.dataScope}

这个肯定是后面拼上的,虽然开起来有注入的问题,但是应该都有解决。我们去看service

  1. @Override
  2. @DataScope(deptAlias = "d")
  3. public List<SysDept> selectDeptList(SysDept dept)
  4. {
  5. return deptMapper.selectDeptList(dept);
  6. }

发现有@DataScope注解。那这个注解什么时候用呢?是Mybatis的拦截器还是AOP呢,应该不会是Mybatis拦截器,如果是的话就不应该有${params.dataScope}这个代码。所以我们去找AOP

最后发现果然是AOP

这里做了两步,第一步是清除dataScode,估计是防止sql注入

  1. /**
  2. * 拼接权限sql前先清空params.dataScope参数防止注入
  3. */
  4. private void clearDataScope(final JoinPoint joinPoint)
  5. {
  6. Object params = joinPoint.getArgs()[0];
  7. if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
  8. {
  9. BaseEntity baseEntity = (BaseEntity) params;
  10. baseEntity.getParams().put(DATA_SCOPE, "");
  11. }
  12. }

这里我们的参数都继承了BaseEntity,因为只有这个父类才params

然后就获取当前登录信息,毕竟要从当前登录信息里面去获取用户的角色权限,哪些角色的数据权限是怎样的,最后要获取用户所有的权限集合。

  1. protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
  2. {
  3. // 获取当前的用户
  4. LoginUser loginUser = SecurityUtils.getLoginUser();
  5. if (StringUtils.isNotNull(loginUser))
  6. {
  7. SysUser currentUser = loginUser.getUser();
  8. // 如果是超级管理员,则不过滤数据
  9. if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
  10. {
  11. String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
  12. dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
  13. }
  14. }
  15. }

当然这里怎么获取用户登录信息,我们可以在controller或者springboot的拦截器过滤器将用户的信息token放入当前线程中,后续Mybatis的拦截器就可以获取了,这里不展开。

然后就各种根据用户选择的角色权限拼凑不同的sql

全部代码

DataScope

  1. package com.ruoyi.common.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. /**
  8. * 数据权限过滤注解
  9. *
  10. * @author ruoyi
  11. */
  12. @Target(ElementType.METHOD)
  13. @Retention(RetentionPolicy.RUNTIME)
  14. @Documented
  15. public @interface DataScope
  16. {
  17. /**
  18. * 部门表的别名
  19. */
  20. public String deptAlias() default "";
  21. /**
  22. * 用户表的别名
  23. */
  24. public String userAlias() default "";
  25. /**
  26. * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来
  27. */
  28. public String permission() default "";
  29. }

DataScopeAspect

  1. package com.ruoyi.framework.aspectj;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.aspectj.lang.JoinPoint;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.annotation.Before;
  7. import org.springframework.stereotype.Component;
  8. import com.ruoyi.common.annotation.DataScope;
  9. import com.ruoyi.common.constant.UserConstants;
  10. import com.ruoyi.common.core.domain.BaseEntity;
  11. import com.ruoyi.common.core.domain.entity.SysRole;
  12. import com.ruoyi.common.core.domain.entity.SysUser;
  13. import com.ruoyi.common.core.domain.model.LoginUser;
  14. import com.ruoyi.common.core.text.Convert;
  15. import com.ruoyi.common.utils.SecurityUtils;
  16. import com.ruoyi.common.utils.StringUtils;
  17. import com.ruoyi.framework.security.context.PermissionContextHolder;
  18. /**
  19. * 数据过滤处理
  20. *
  21. * @author ruoyi
  22. */
  23. @Aspect
  24. @Component
  25. public class DataScopeAspect
  26. {
  27. /**
  28. * 全部数据权限
  29. */
  30. public static final String DATA_SCOPE_ALL = "1";
  31. /**
  32. * 自定数据权限
  33. */
  34. public static final String DATA_SCOPE_CUSTOM = "2";
  35. /**
  36. * 部门数据权限
  37. */
  38. public static final String DATA_SCOPE_DEPT = "3";
  39. /**
  40. * 部门及以下数据权限
  41. */
  42. public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
  43. /**
  44. * 仅本人数据权限
  45. */
  46. public static final String DATA_SCOPE_SELF = "5";
  47. /**
  48. * 数据权限过滤关键字
  49. */
  50. public static final String DATA_SCOPE = "dataScope";
  51. @Before("@annotation(controllerDataScope)")
  52. public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable
  53. {
  54. clearDataScope(point);
  55. handleDataScope(point, controllerDataScope);
  56. }
  57. protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope)
  58. {
  59. // 获取当前的用户
  60. LoginUser loginUser = SecurityUtils.getLoginUser();
  61. if (StringUtils.isNotNull(loginUser))
  62. {
  63. SysUser currentUser = loginUser.getUser();
  64. // 如果是超级管理员,则不过滤数据
  65. if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin())
  66. {
  67. String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());
  68. dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission);
  69. }
  70. }
  71. }
  72. /**
  73. * 数据范围过滤
  74. *
  75. * @param joinPoint 切点
  76. * @param user 用户
  77. * @param deptAlias 部门别名
  78. * @param userAlias 用户别名
  79. * @param permission 权限字符
  80. */
  81. public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission)
  82. {
  83. StringBuilder sqlString = new StringBuilder();
  84. List<String> conditions = new ArrayList<String>();
  85. List<String> scopeCustomIds = new ArrayList<String>();
  86. user.getRoles().forEach(role -> {
  87. if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
  88. {
  89. scopeCustomIds.add(Convert.toStr(role.getRoleId()));
  90. }
  91. });
  92. for (SysRole role : user.getRoles())
  93. {
  94. String dataScope = role.getDataScope();
  95. if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE))
  96. {
  97. continue;
  98. }
  99. if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission)))
  100. {
  101. continue;
  102. }
  103. if (DATA_SCOPE_ALL.equals(dataScope))
  104. {
  105. sqlString = new StringBuilder();
  106. conditions.add(dataScope);
  107. break;
  108. }
  109. else if (DATA_SCOPE_CUSTOM.equals(dataScope))
  110. {
  111. if (scopeCustomIds.size() > 1)
  112. {
  113. // 多个自定数据权限使用in查询,避免多次拼接。
  114. sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds)));
  115. }
  116. else
  117. {
  118. sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId()));
  119. }
  120. }
  121. else if (DATA_SCOPE_DEPT.equals(dataScope))
  122. {
  123. sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
  124. }
  125. else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope))
  126. {
  127. sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId()));
  128. }
  129. else if (DATA_SCOPE_SELF.equals(dataScope))
  130. {
  131. if (StringUtils.isNotBlank(userAlias))
  132. {
  133. sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
  134. }
  135. else
  136. {
  137. // 数据权限为仅本人且没有userAlias别名不查询任何数据
  138. sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
  139. }
  140. }
  141. conditions.add(dataScope);
  142. }
  143. // 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
  144. if (StringUtils.isEmpty(conditions))
  145. {
  146. sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
  147. }
  148. if (StringUtils.isNotBlank(sqlString.toString()))
  149. {
  150. Object params = joinPoint.getArgs()[0];
  151. if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
  152. {
  153. BaseEntity baseEntity = (BaseEntity) params;
  154. baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
  155. }
  156. }
  157. }
  158. /**
  159. * 拼接权限sql前先清空params.dataScope参数防止注入
  160. */
  161. private void clearDataScope(final JoinPoint joinPoint)
  162. {
  163. Object params = joinPoint.getArgs()[0];
  164. if (StringUtils.isNotNull(params) && params instanceof BaseEntity)
  165. {
  166. BaseEntity baseEntity = (BaseEntity) params;
  167. baseEntity.getParams().put(DATA_SCOPE, "");
  168. }
  169. }
  170. }

四、总结

上面只是初步了解,还是很强大的。

 19

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2