一.若依是什么

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 install

npm run dev

2.2 后端搭建

老生常谈、修改数据库和redis。 运行成功!

三.代码介绍

1.ruoyi-quartz

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

2.ruoyi-generator

该包为代码生成器,主要的流程如下。 1.GenTableServiceImpl/generatorCode代码生成

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

@Override

public void generatorCode(String tableName)

{

// 查询表信息

GenTable table = genTableMapper.selectGenTableByName(tableName);

// 设置主子表信息

setSubTable(table);

// 设置主键列信息

setPkColumn(table);

VelocityInitializer.initVelocity();

VelocityContext context = VelocityUtils.prepareContext(table);

// 获取模板列表

List 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类型

*/

@InitBinder

public void initBinder(WebDataBinder binder)

{

// Date 类型转换

binder.registerCustomEditor(Date.class, new PropertyEditorSupport()

{

@Override

public 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

{

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 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)

{

//拼接sql

StringBuilder 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 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路径的可配置化。

@Bean

public 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);

//加入mapper

sessionFactory.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

{

@Autowired

private RepeatSubmitInterceptor repeatSubmitInterceptor;

@Override

public 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/");

}

//配置拦截器生效

@Override

public void addInterceptors(InterceptorRegistry registry)

{ //此处配置了上文点击重复的拦截器

registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**");

}

//跨域配置

@Bean

public 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);

// 返回新的CorsFilter

return 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 nowDataMap = new HashMap();

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值查询redsi

Object sessionObj = redisCache.getCacheObject(cacheRepeatKey);

//如果能够查询到

if (sessionObj != null)

{

Map sessionMap = (Map) sessionObj;

if (sessionMap.containsKey(url))

{

Map preDataMap = (Map) sessionMap.get(url);

//比对参数,同时比对时间

if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval()))

{

return true;

}

}

}

Map cacheMap = new HashMap();

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

{

@Autowired

private TokenService tokenService;

@Override

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)

throws ServletException, IOException

{

//验证用户信息

LoginUser loginUser = tokenService.getLoginUser(request);

if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))

{

//刷新token

tokenService.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了。架构的常用写法。

@RestControllerAdvice

public 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 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

{

@Autowired

private RedisTemplate redisTemplate;

@PreAuthorize("@ss.hasPermi('monitor:cache:list')")

@GetMapping()

public AjaxResult getInfo() throws Exception

{

//redis的常用信息

Properties info = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info());

Properties commandStats = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info("commandstats"));

//rediskey数量

Object dbSize = redisTemplate.execute((RedisCallback) connection -> connection.dbSize());

Map result = new HashMap<>(3);

result.put("info", info);

result.put("dbSize", dbSize);

//key的详细信息

List> pieList = new ArrayList<>();

commandStats.stringPropertyNames().forEach(key -> {

Map 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 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

{

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对于代码的规范和写法非常友好,同时注释也比较到位,代码很干净和舒服。这点爆赞!

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

精彩链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: 

发表评论

返回顶部暗黑模式