一.若依是什么

1.1 若依能干什么

当您在开发企业级项目时,除了用户提供的特定业务需要开发外还有一些通用的架构功能

1.2 若依适合什么时候使用

功能很简单,提供了基础的系统管理,监控与代码生成。

1.3 若依提供了那些功能

1.3.1 系统管理

用户管理:用户是系统操作者,该功能主要完成系统用户配置。
部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
岗位管理:配置系统用户所属担任职务。
菜单管理:配置系统菜单,操作权限,按钮权限标识等。
角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
字典管理:对系统中经常使用的一些较为固定的数据进行维护。
参数管理:对系统动态配置常用参数。
通知公告:系统通知公告信息发布维护。

1.3.2 日志管理

操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
登录日志:系统登录日志记录查询包含登录异常。

1.3.3 系统监控

在线用户:当前系统中活跃用户状态监控。
服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。
缓存监控:对系统的缓存查询,查看、清理等操作。
在线构建器:拖动表单元素生成相应的HTML代码。
连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。

1.3.4 系统工具

定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。
系统接口:根据业务代码自动生成相关的api接口文档。

二.快速开始若依

2.1 前端搭建

npm installnpm run dev

2.2 后端搭建

老生常谈、修改数据库和redis。


运行成功!

三.代码介绍

1.ruoyi-quartz

使用quartz作为定时任务管理,这里不多说,老生常谈的问题。如果想了解可以参考文章:quartz实现定时任务

2.ruoyi-generator

该包为代码生成器,主要的流程如下。

1.GenTableServiceImpl/generatorCode代码生成

核心代码为该类的方法。主要使用org.apache.velocity.app的API。

@Overridepublic void generatorCode(String tableName){// 查询表信息GenTable table = genTableMapper.selectGenTableByName(tableName);// 设置主子表信息setSubTable(table);// 设置主键列信息setPkColumn(table);VelocityInitializer.initVelocity();VelocityContext context = VelocityUtils.prepareContext(table);// 获取模板列表List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory());for (String template : templates){if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")){// 渲染模板StringWriter sw = new StringWriter();Template tpl = Velocity.getTemplate(template, Constants.UTF8);tpl.merge(context, sw);try{String path = getGenPath(table, template);FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8);}catch (IOException e){throw new ServiceException("渲染模板失败,表名:" + table.getTableName());}}}}

3.ruoyi-system


标准的增删改查方法。没有什么多说的。但是ruoyi项目将controller与service部分隔离开来。

4.ruoyi-common

1.annotation

自定义注解,注解的功能在下方。

2.config

获取application.yml中的配置信息,并注入项目bean中。

3.constant

为项目提供常量池。

4.core

1.controller

所有接口层的基类,提供了分页,排序等方法,其他接口类直接继承即可。架构的常用做法。

public class BaseController{protected final Logger logger = LoggerFactory.getLogger(this.getClass());/** * 将前台传递过来的日期格式的字符串,自动转化为Date类型 */@InitBinderpublic void initBinder(WebDataBinder binder){// Date 类型转换binder.registerCustomEditor(Date.class, new PropertyEditorSupport(){@Overridepublic void setAsText(String text){setValue(DateUtils.parseDate(text));}});}/** * 设置请求分页数据 */protected void startPage(){PageDomain pageDomain = TableSupport.buildPageRequest();Integer pageNum = pageDomain.getPageNum();Integer pageSize = pageDomain.getPageSize();if (StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)){String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());Boolean reasonable = pageDomain.getReasonable();PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);}}/** * 设置请求排序数据 */protected void startOrderBy(){PageDomain pageDomain = TableSupport.buildPageRequest();if (StringUtils.isNotEmpty(pageDomain.getOrderBy())){String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());PageHelper.orderBy(orderBy);}}/** * 响应请求分页数据 */@SuppressWarnings({ "rawtypes", "unchecked" })protected TableDataInfo getDataTable(List<" />> list){TableDataInfo rspData = new TableDataInfo();rspData.setCode(HttpStatus.SUCCESS);rspData.setMsg("查询成功");rspData.setRows(list);rspData.setTotal(new PageInfo(list).getTotal());return rspData;}...

2.domain

权限等架构查询使用的实体类集合,其中BaseEntity、AjaxResult为架构设计经常使用的。

BaseEntity需要其他实体类继承,提供了页码、总数等通用字段。

AjaxResult是统一返回的实体类,能够与前台约定好固定的返回格式。{code: message: data}格式。

public class AjaxResult extends HashMap<String, Object>{private static final long serialVersionUID = 1L;/** 状态码 */public static final String CODE_TAG = "code";/** 返回内容 */public static final String MSG_TAG = "msg";/** 数据对象 */public static final String DATA_TAG = "data";/** * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 */public AjaxResult(){}/** * 初始化一个新创建的 AjaxResult 对象 ** @param code 状态码 * @param msg 返回内容 */public AjaxResult(int code, String msg){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/** * 初始化一个新创建的 AjaxResult 对象 ** @param code 状态码 * @param msg 返回内容 * @param data 数据对象 */public AjaxResult(int code, String msg, Object data){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (StringUtils.isNotNull(data)){super.put(DATA_TAG, data);}}...public class BaseEntity implements Serializable{private static final long serialVersionUID = 1L;/** 搜索值 */private String searchValue;/** 创建者 */private String createBy;/** 创建时间 */@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date createTime;/** 更新者 */private String updateBy;/** 更新时间 */@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private Date updateTime;/** 备注 */private String remark;/** 请求参数 */private Map<String, Object> params;...

5.enums

枚举类,这里不多说。

6.exception

封装了一系列的异常,在特定时期使用即可。

7.filter

过滤器,通用写法,如果有使用直接复制即可。

8.utils

提供了一大波工具类。如果需要可以直接复制使用。

5.ruoyi-framework

1.DataScopeAspect

1.DataScopeAspect数据权限

在执行接口时,将当前用户的组织机构等查询条件利用AOP拼接上,可以看到根据在菜单页面维护的数据权限类别进行动态的拼接sql语句。核心代码如下:

public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias){//拼接sqlStringBuilder sqlString = new StringBuilder();for (SysRole role : user.getRoles()){String dataScope = role.getDataScope();if (DATA_SCOPE_ALL.equals(dataScope)){sqlString = new StringBuilder();break;}//自定义权限拼接else if (DATA_SCOPE_CUSTOM.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,role.getRoleId()));}//部门权限拼接else if (DATA_SCOPE_DEPT.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));}//部门及以下权限拼接else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)){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()));}...

2.DataSourceAspect多数据源

利用上文DataSource注解动态的切换数据源,如果有需求可以直接使用,固定写法。

3.LogAspect日志

全局日志收集,如用户姓名、接口方法、调用ip等,并插入数据库,比较通用功能。

protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult){try{// 获取当前的用户LoginUser loginUser = SecurityUtils.getLoginUser();// *========数据库日志=========*//SysOperLog operLog = new SysOperLog();operLog.setStatus(BusinessStatus.SUCCESS.ordinal());// 请求的地址String ip = IpUtils.getIpAddr(ServletUtils.getRequest());operLog.setOperIp(ip);operLog.setOperUrl(ServletUtils.getRequest().getRequestURI());//获取用户姓名if (loginUser != null){operLog.setOperName(loginUser.getUsername());}if (e != null){operLog.setStatus(BusinessStatus.FAIL.ordinal());operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));}// 设置方法名称String className = joinPoint.getTarget().getClass().getName();String methodName = joinPoint.getSignature().getName();operLog.setMethod(className + "." + methodName + "()");// 设置请求方式operLog.setRequestMethod(ServletUtils.getRequest().getMethod());// 处理设置注解上的参数getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);// 保存数据库AsyncManager.me().execute(AsyncFactory.recordOper(operLog));}catch (Exception exp){// 记录本地异常日志log.error("==前置通知异常==");log.error("异常信息:{}", exp.getMessage());exp.printStackTrace();}}

4.RateLimiterAspect限流

将每一次的调用的ip存放在redis中,然后判断本次调用和上次调用的相隔时间。短时间调用会阻止调用。

@Before("@annotation(rateLimiter)")public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable{//redis固定的参数String key = rateLimiter.key();int time = rateLimiter.time();int count = rateLimiter.count();//获取ip+调用的方法String combineKey = getCombineKey(rateLimiter, point);List<Object> keys = Collections.singletonList(combineKey);try{//获取一定时间内的调用次数Long number = redisTemplate.execute(limitScript, keys, count, time);if (StringUtils.isNull(number) || number.intValue() > count){throw new ServiceException("访问过于频繁,请稍候再试");}log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), key);}catch (ServiceException e){throw e;}catch (Exception e){throw new RuntimeException("服务器限流异常,请稍候再试");}}

2.config


1.DruidProperties

从application.yml中获取数据源信息。固定写法不详细描述。

2.ApplicationConfig

配置时区信息这里不详细描述。

3.CaptchaConfig

验证码配置,配置文字文本框格式等,固定写法。

4.DruidConfig

多数据源配置,固定写法。

5.FastJson2JsonRedisSerializer

redis序列化配置,固定写法。

6.FilterConfig

过滤器配置,@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")根据application.yml是否配置xss.enabled值决定是否加载该类,也就是是否开启xss拦截器。

7.KaptchaTextCreator

验证码验证的规则,这里是计算验证码,逻辑在此类中,不详细讲解。

8.MyBatisConfig

该类为从application.yml动态获取mybatis包的地址。并重新封装SqlSessionFactory。实现mybatis路径的可配置化。

@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception{//从application.yml获取配置String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");String mapperLocations = env.getProperty("mybatis.mapperLocations");String configLocation = env.getProperty("mybatis.configLocation");//获取实体类的包typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);VFS.addImplClass(SpringBootVFS.class);final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();//加入数据源sessionFactory.setDataSource(dataSource);//加入实体类地址sessionFactory.setTypeAliasesPackage(typeAliasesPackage);//加入mappersessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));//加入配置文件地址sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));return sessionFactory.getObject();}

9.RedisConfig

redis配置固定写法,如果整合redis可以参考。

10.ResourcesConfig

通用配置,其中包括拦截器生效配置,跨域配置等 可以直接使用。

public class ResourcesConfig implements WebMvcConfigurer{@Autowiredprivate RepeatSubmitInterceptor repeatSubmitInterceptor;@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry){/** 本地文件上传路径 */registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**").addResourceLocations("file:" + RuoYiConfig.getProfile() + "/");/** swagger配置 */registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");}//配置拦截器生效@Overridepublic void addInterceptors(InterceptorRegistry registry){ //此处配置了上文点击重复的拦截器registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");}//跨域配置@Beanpublic CorsFilter corsFilter(){CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(true);// 设置访问源地址config.addAllowedOriginPattern("*");// 设置访问源请求头config.addAllowedHeader("*");// 设置访问源请求方法config.addAllowedMethod("*");// 有效期 1800秒config.setMaxAge(1800L);// 添加映射路径,拦截一切请求UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", config);// 返回新的CorsFilterreturn new CorsFilter(source);}}

10 SecurityConfig

Security的配置,如果想详细了解,请访问博主专栏:Security入门到精通。

11.ServerConfig

获取请求信息,包括:域名,端口,上下文访问路径

12.ThreadPoolConfig

线程池配置,固定配置,其中下文的manager使用了异步线程池。

3.datasource

多数据源固定配置,与DataSourceAspect配合使用。

4.interceptor

主要功能为不允许重复点击,主要实现为将每一次的调用信息组装为key值存放到redis中,每一次调用从redis获取该key数据验证相隔时间,核心代码如下。

public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation){String nowParams = "";if (request instanceof RepeatedlyRequestWrapper){RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request;nowParams = HttpHelper.getBodyString(repeatedlyRequest);}// body参数为空,获取Parameter的数据if (StringUtils.isEmpty(nowParams)){nowParams = JSONObject.toJSONString(request.getParameterMap());}Map<String, Object> nowDataMap = new HashMap<String, Object>();nowDataMap.put(REPEAT_PARAMS, nowParams);nowDataMap.put(REPEAT_TIME, System.currentTimeMillis());// 请求地址(作为存放cache的key值)String url = request.getRequestURI();// 唯一值(没有消息头则使用请求地址)String submitKey = request.getHeader(header);if (StringUtils.isEmpty(submitKey)){submitKey = url;}// 组装成加入redis的key值String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;//根据key值查询redsiObject sessionObj = redisCache.getCacheObject(cacheRepeatKey);//如果能够查询到if (sessionObj != null){Map<String, Object> sessionMap = (Map<String, Object>) sessionObj;if (sessionMap.containsKey(url)){Map<String, Object> preDataMap = (Map<String, Object>) sessionMap.get(url);//比对参数,同时比对时间if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())){return true;}}}Map<String, Object> cacheMap = new HashMap<String, Object>();cacheMap.put(url, nowDataMap);redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS);return false;}

5.manager


这里的AsyncManager、ShutdownManager为异步工厂提供方法,AsyncFactory为如何使用异步,如果需要使用可以直接在类中参考编写。

6.security


1.JwtAuthenticationTokenFilter

主要为验证token是否正确,

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter{@Autowiredprivate TokenService tokenService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException{//验证用户信息LoginUser loginUser = tokenService.getLoginUser(request);if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())){//刷新tokentokenService.verifyToken(loginUser);//获取用户权限对象UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));//将用户权限等信息存放在SecurityContext中SecurityContextHolder.getContext().setAuthentication(authenticationToken);}chain.doFilter(request, response);}}

2.AuthenticationEntryPointImpl、LogoutSuccessHandlerImpl

将调用信息转换为json返回的固定写法。如果有需要可以参考。

7.web


1.server

获取服务器信息,如cpu等,都是固定写法,如需参考直接复制即可。

private void setCpuInfo(CentralProcessor processor){// CPU信息long[] prevTicks = processor.getSystemCpuLoadTicks();Util.sleep(OSHI_WAIT_SECOND);long[] ticks = processor.getSystemCpuLoadTicks();long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;cpu.setCpuNum(processor.getLogicalProcessorCount());cpu.setTotal(totalCpu);cpu.setSys(cSys);cpu.setUsed(user);cpu.setWait(iowait);cpu.setFree(idle);}/** * 设置内存信息 */private void setMemInfo(GlobalMemory memory){mem.setTotal(memory.getTotal());mem.setUsed(memory.getTotal() - memory.getAvailable());mem.setFree(memory.getAvailable());}...

2.exception

@RestControllerAdvice、@ExceptionHandler全局的拦截异常,当系统中有异常时,该类会直接获取异常并输出,很多类就不用特意try catch了。架构的常用写法。

@RestControllerAdvicepublic class GlobalExceptionHandler{private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);/** * 权限校验异常 */@ExceptionHandler(AccessDeniedException.class)public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request){String requestURI = request.getRequestURI();log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");}/** * 请求方式不支持 */@ExceptionHandler(HttpRequestMethodNotSupportedException.class)public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,HttpServletRequest request){String requestURI = request.getRequestURI();log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());return AjaxResult.error(e.getMessage());}

3.service

权限相关的service。大部分都是curd的业务,这里详细说TokenService。

1.TokenService

其中TokenService使用jwt生成token、从token中获取数据等。大部分为jwt固定写法。

/** * 从数据声明生成令牌 * * @param claims 数据声明 * @return 令牌 */private String createToken(Map<String, Object> claims){String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();return token;}/** * 从令牌中获取数据声明 * * @param token 令牌 * @return 数据声明 */private Claims parseToken(String token){return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();}

6.ruoyi-admin

1.common通用方法

1.CaptchaController

获取验证码通过API生成验证码。详细代码不进行讲解。

2.CommonController

通用的上传下载接口,如果需要直接使用即可。

2.monitor监控

1.CacheController监控redis

public class CacheController{@Autowiredprivate RedisTemplate<String, String> redisTemplate;@PreAuthorize("@ss.hasPermi('monitor:cache:list')")@GetMapping()public AjaxResult getInfo() throws Exception{//redis的常用信息Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));//rediskey数量Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());Map<String, Object> result = new HashMap(3);result.put("info", info);result.put("dbSize", dbSize);//key的详细信息List<Map<String, String>> pieList = new ArrayList();commandStats.stringPropertyNames().forEach(key -> {Map<String, String> data = new HashMap(2);String property = commandStats.getProperty(key);data.put("name", StringUtils.removeStart(key, "cmdstat_"));data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));pieList.add(data);});result.put("commandStats", pieList);return AjaxResult.success(result);}}

2.ServerController服务器监控

主要监控正在运行服务器的信息,核心代码如下,如果需要使用直接复制即可。

public void copyTo() throws Exception{SystemInfo si = new SystemInfo();HardwareAbstractionLayer hal = si.getHardware();setCpuInfo(hal.getProcessor());setMemInfo(hal.getMemory());setSysInfo();setJvmInfo();setSysFiles(si.getOperatingSystem());}/** * 设置CPU信息 */private void setCpuInfo(CentralProcessor processor){// CPU信息long[] prevTicks = processor.getSystemCpuLoadTicks();Util.sleep(OSHI_WAIT_SECOND);long[] ticks = processor.getSystemCpuLoadTicks();long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()];long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()];long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()];long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()];long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()];long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()];long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()];long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()];long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;cpu.setCpuNum(processor.getLogicalProcessorCount());cpu.setTotal(totalCpu);cpu.setSys(cSys);cpu.setUsed(user);cpu.setWait(iowait);cpu.setFree(idle);}/** * 设置内存信息 */private void setMemInfo(GlobalMemory memory){mem.setTotal(memory.getTotal());mem.setUsed(memory.getTotal() - memory.getAvailable());mem.setFree(memory.getAvailable());}/** * 设置服务器信息 */private void setSysInfo(){Properties props = System.getProperties();sys.setComputerName(IpUtils.getHostName());sys.setComputerIp(IpUtils.getHostIp());sys.setOsName(props.getProperty("os.name"));sys.setOsArch(props.getProperty("os.arch"));sys.setUserDir(props.getProperty("user.dir"));}/** * 设置Java虚拟机 */private void setJvmInfo() throws UnknownHostException{Properties props = System.getProperties();jvm.setTotal(Runtime.getRuntime().totalMemory());jvm.setMax(Runtime.getRuntime().maxMemory());jvm.setFree(Runtime.getRuntime().freeMemory());jvm.setVersion(props.getProperty("java.version"));jvm.setHome(props.getProperty("java.home"));}/** * 设置磁盘信息 */private void setSysFiles(OperatingSystem os){FileSystem fileSystem = os.getFileSystem();List<OSFileStore> fsArray = fileSystem.getFileStores();for (OSFileStore fs : fsArray){long free = fs.getUsableSpace();long total = fs.getTotalSpace();long used = total - free;SysFile sysFile = new SysFile();sysFile.setDirName(fs.getMount());sysFile.setSysTypeName(fs.getType());sysFile.setTypeName(fs.getName());sysFile.setTotal(convertFileSize(total));sysFile.setFree(convertFileSize(free));sysFile.setUsed(convertFileSize(used));sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100));sysFiles.add(sysFile);}}

3.SysLogininforController,SysOperlogController 登录日志、操作日志

主要查询前文AOP生成的日志表,普通的增删改查。

4.SysUserOnlineController在线用户管理

主要功能为在线用户监控与强踢下线。通过查询和删除redis缓存即可实现。

3.system业务代码

这里都是系统管理业务代码,写法比较统一,但是在编写过程中有部分架构的规定,下面一一说明。

1.@PreAuthorize(“@ss.hasPermi(‘system:dict:list’)”)

权限注解,上文已经详解

2.AjaxResult

提供了 结果编码/调用信息/数据的返回格式,为前台提供了统一的返回格式。架构的基础组成部分。

public class AjaxResult extends HashMap<String, Object>{private static final long serialVersionUID = 1L;/** 状态码 */public static final String CODE_TAG = "code";/** 返回内容 */public static final String MSG_TAG = "msg";/** 数据对象 */public static final String DATA_TAG = "data";/** * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 */public AjaxResult(){}/** * 初始化一个新创建的 AjaxResult 对象 ** @param code 状态码 * @param msg 返回内容 */public AjaxResult(int code, String msg){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);}/** * 初始化一个新创建的 AjaxResult 对象 ** @param code 状态码 * @param msg 返回内容 * @param data 数据对象 */public AjaxResult(int code, String msg, Object data){super.put(CODE_TAG, code);super.put(MSG_TAG, msg);if (StringUtils.isNotNull(data)){super.put(DATA_TAG, data);}}/** * 返回成功消息 ** @return 成功消息 */public static AjaxResult success(){return AjaxResult.success("操作成功");}...

3.extends BaseController

上文介绍了BaseController,在这里使用就可以直接调用分页、排序等方法了。不用每个类都进行编写。

四.总结

ruoyi框架搭建方便,依赖组件非常少。同时提供了基本的业务功能,如用户管理、部门管理、代码生成器等,但是对于技术的深度还是不太到位,如mq的使用,安全框架等技术都没有特多的涉及。

但是ruoyi对于代码的规范和写法非常友好,同时注释也比较到位,代码很干净和舒服。这点爆赞!

所以这里非常推荐使用作为接私活和企业基础架构。如果能合理的增加扩展功能就更好啦。