Ruoyi architecture that something

1. What is Zoe?

1.1 What Zoyi Can Do

When you are developing an enterprise-level project, in addition to the development of specific business needs provided by users, there are also some general architecture functions

1.2 When is it suitable for use

The function is very simple, providing basic system management, monitoring and code generation.

1.3 What functions does Zoyi provide

1.3.1 System Management

User management: The user is the system operator, and this function mainly completes the system user configuration.
Department management: Configure the system organization (company, department, group), and the tree structure shows the support data permissions.
Position management: configure the positions that system users belong to.
Menu management: configure the system menu, operation authority, button authority identification, etc.
Role management: role menu authority assignment, role setting, data range authority division by organization.
Dictionary management: maintain some relatively fixed data frequently used in the system.
Parameter management: dynamically configure common parameters for the system.
Notification Announcement: The system notifies and announces the release and maintenance of information.

1.3.2 Log Management

Operation log: system normal operation log record and query; system abnormal information log record and query.
Login Logs: System login logging queries contain login exceptions.

1.3.3 System Monitoring

Online users: monitor the status of active users in the current system.
Service monitoring: monitor the current system CPU, memory, disk, stack and other related information.
Cache monitoring: query, view, clear and other operations on the system cache.
Online Builder: Drag form elements to generate corresponding HTML code.
Connection pool monitoring: monitor the status of the current system database connection pool, and analyze SQL to find system performance bottlenecks.

1.3.4 System tools

Scheduled tasks: Online (add, modify, delete) task scheduling includes execution result logs.
Code generation: front-end and back-end code generation (java, html, xml, sql) supports CRUD download.
System interface: automatically generate relevant API interface documents according to the business code.

2. Quick Start Ruoyi

2.1 Front-end construction

npm install
npm run dev

insert image description here

2.2 Backend construction

Trivial, modifying databases and redis.
insert image description here
insert image description here
Run successfully!
insert image description here

3. Code introduction

insert image description here

1.ruoyi-quartz

Using quartz as a timed task management, not much to say here, is a commonplace problem. If you want to know, you can refer to the article: Quartz implements timing tasks

2. ruoyi-generator

This package is a code generator, the main process is as follows.
insert image description here
1.GenTableServiceImpl/generatorCode code generation

The core code is the method of this class. Mainly use the API of org.apache.velocity.app .

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

insert image description here
Standard CRUD method. There is not much to say. But the ruoyi project isolates the controller from the service part.

4.ruoyi-common

insert image description here

1.annotation

Custom annotations, the functions of annotations are below.
insert image description here

2.config

Get the configuration information in application.yml and inject it into the project bean.
insert image description here

3.constant

Provides a constant pool for the project.
insert image description here

4.core

1.controller

The base class of all interface layers provides methods such as paging and sorting, and other interface classes can be directly inherited. Common practice for architecture.

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

A collection of entity classes used by schema queries such as permissions, which are BaseEntity、AjaxResultoften used for schema design.

BaseEntity needs to be inherited by other entity classes, and provides common fields such as page number and total number.

AjaxResult is an entity class that is returned uniformly, and can agree on a fixed return format with the front desk. {code: message: data}Format.

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

Enumeration class, not much to say here.

6.exception

It encapsulates a series of exceptions and can be used in a specific period.
insert image description here

7.filter

Filter, general wording, if you use it, just copy it directly.
insert image description here

8.utils

A large number of tools are provided. It can be directly copied and used if necessary.
insert image description here

5. ruoyi-framework

insert image description here

1.DataScopeAspect

1. DataScopeAspect data permissions

When executing the interface, use AOP to splice the query conditions such as the current user's organization, and you can see the dynamic splicing of SQL statements according to the data permission category maintained on the menu page. The core code is as follows:

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 multiple data sources

Use the above DataSource annotation to dynamically switch data sources. If there is a need, you can use it directly, and the writing method is fixed.

3. LogAspect log

Collect global logs, such as user name, interface method, calling ip, etc., and insert them into the database to compare common functions.

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 current limiting

Store the ip of each call in redis, and then judge the time interval between this call and the last call. A short call blocks the call.

@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

insert image description here
1.DruidProperties

Obtain data source information from application.yml. The fixed writing method is not described in detail.

2.ApplicationConfig

Configuring time zone information is not described in detail here.

3.CaptchaConfig

Verification code configuration, configuration text box format, etc., fixed writing.

4.DruidConfig

Multiple data source configuration, fixed writing.

5.FastJson2JsonRedisSerializer

Redis serialization configuration, fixed writing.

6.FilterConfig

Filter configuration, @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")according to whether application.yml is configured with xss.enabled value determines whether to load the class, that is, whether to enable the xss interceptor.

7.KaptchaTextCreator

The rules of verification code verification, here is to calculate the verification code, the logic is in this category, and will not be explained in detail.

8.MyBatisConfig

This class dynamically obtains the address of the mybatis package from application.yml. And repackage SqlSessionFactory. Realize the configurability of mybatis path.

@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 configuration fixed writing method, if you integrate redis, you can refer to it.

10.ResourcesConfig

General configuration, including interceptor effective configuration, cross-domain configuration, etc. can be used directly.

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 configuration, if you want to know more about it, please visit the blogger's column: Security entry to proficiency.

11.ServerConfig

Obtain request information, including: domain name, port, context access path

12.ThreadPoolConfig

Thread pool configuration, fixed configuration, where the manager below uses an asynchronous thread pool.

3.datasource

Fixed configuration of multiple data sources, used in conjunction with DataSourceAspect.
insert image description here

4.interceptor

The main function is not to allow repeated clicks. The main implementation is to assemble each call information into a key value and store it in redis. Each call obtains the key data from redis to verify the interval. The core code is as follows.

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

insert image description here
The methods here AsyncManager、ShutdownManagerare provided for the asynchronous factory, and AsyncFactory is how to use asynchrony. If you need to use it, you can refer to it directly in the class.

6.security

insert image description here
1.JwtAuthenticationTokenFilter

Mainly to verify whether the token is correct,

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

Convert the call information to the fixed writing method returned by json. Refer to it if necessary.
insert image description here

7.web

insert image description here
1.server

Obtaining server information, such as cpu, etc., is written in a fixed way, if you need to refer to it, just copy it directly.

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、@ExceptionHandlerGlobally intercept exceptions. When there is an exception in the system, this class will directly obtain the exception and output it. Many classes do not need to try catch specially. A common way of writing architecture.

@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

Permission-related services. Most of them are curd's business, here is the TokenService in detail.

1.TokenService

Among them, TokenService uses jwt to generate token, obtain data from token, and so on. Most of them are jwt fixed writing.

/**
     * 从数据声明生成令牌
     *
     * @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

insert image description here

1.common general method

1.CaptchaController

Get Verification Code Generate a verification code through the API. The detailed code will not be explained.

2.CommonController

The general upload and download interface can be used directly if needed.

2. monitor monitoring

1. CacheController monitors redis

public class CacheController
{
    
    
    @Autowired
    private 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 server monitoring

It mainly monitors the information of the running server. The core code is as follows, if you need to use direct copy.

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 login log, operation log

Mainly query the log table generated by the previous AOP, and perform common additions, deletions, and modifications.

4. SysUserOnlineController online user management

The main functions are online user monitoring and kicking offline. This can be achieved by querying and deleting the redis cache.

3.system business code

Here are all system management business codes, and the writing method is relatively uniform, but there are some architectural regulations during the writing process, which will be explained one by one below.
insert image description here
1. @PreAuthorize("@ss.hasPermi('system:dict:list')")

Permission annotation, which has been explained in detail above

2.AjaxResult

Provides the return format of result encoding/call information/data, and provides a unified return format for the front desk. A fundamental component of the architecture.

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

As mentioned above BaseController, if you use it here, you can directly call methods such as paging and sorting. Don't write every class.

Four. Summary

The ruoyi framework is easy to build, with very few dependent components. At the same time, it provides basic business functions, such as user management, department management, code generator, etc., but the depth of technology is still not in place, such as the use of mq, security framework and other technologies are not particularly involved.

But ruoyi is very friendly to the standardization and writing of the code, and the comments are also in place, the code is very clean and comfortable. This is awesome!

Therefore, it is highly recommended to use it as a private job and enterprise infrastructure. It would be even better if the extension function can be added reasonably.

Guess you like

Origin blog.csdn.net/qq_20143059/article/details/131337812