十、前后端分离通用权限系统(10)

news2024/11/16 12:37:42

🌻🌻 目录

  • 一、功能说明
  • 二、部门管理
  • 三、岗位管理
  • 四、日志管理
    • 4.1、登录日志
      • 4.1.1、功能实现- 接口
      • 4.1.2、编写 AsyncLoginLogMapper
      • 4.1.3、功能实现- 接口实现
      • 4.1.4、导入获取ip地址的工具类 IpUtil
      • 4.1.5、在 TokenLoginFilter 调用方法实现
      • 4.1.6、修改 WebSecurityConfig
      • 4.1.7、测试
    • 4.2、操作日志实现
      • 4.2.1、创建common-log 模块
      • 4.2.2、创建自定义注解Log
      • 4.2.3、创建AOP类 LogAspect
      • 4.2.4、创建枚举类
      • 4.2.3、创建AOP类LogAspect
      • 4.2.4、创建接口 AsyncOperLogService
      • 4.2.5、编写mapper
      • 4.2.6、编写接口实现
      • 4.2.7、进行简单测试
  • 五、项目部署
    • 5.1 后端打包
    • 5.2 前端打包

一、功能说明

其他功能:部门管理、岗位管理、日志管理(登录日志、操作日志)

二、部门管理

实现方式与菜单管理类似

三、岗位管理

实现方式与菜单管理类似

四、日志管理

4.1、登录日志

4.1.1、功能实现- 接口

在spring-security 模块下面创建包 com.gansu.system.service 再创建 AsyncLoginLogService

在这里插入图片描述

package com.gansu.system.service;

public interface AsyncLoginLogService {

    public void recordLoginLog(String username,String ipaddr,Integer status,String message);
}

4.1.2、编写 AsyncLoginLogMapper

AsyncLoginLogMapper

在这里插入图片描述

package com.gansu.system.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gansu.model.system.SysLoginLog;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface AsyncLoginLogMapper extends BaseMapper<SysLoginLog> {

    public void recordLoginLog(String username,String ipaddr,Integer status,String message);
}

4.1.3、功能实现- 接口实现

service-system实现添加日志方法

在这里插入图片描述

package com.gansu.system.service.impl;

import com.gansu.model.system.SysLoginLog;
import com.gansu.system.mapper.AsyncLoginLogMapper;
import com.gansu.system.service.AsyncLoginLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class AsyncLoginLogServiceImpl implements AsyncLoginLogService {

    @Autowired
    private AsyncLoginLogMapper asyncLoginLogMapper;
    @Override
    public void recordLoginLog(String username, String ipaddr, Integer status, String message) {

        SysLoginLog sysLoginLog = new SysLoginLog();

        sysLoginLog.setUsername(username);
        sysLoginLog.setIpaddr(ipaddr);
        sysLoginLog.setStatus(status);
        sysLoginLog.setMsg(message);

        asyncLoginLogMapper.insert(sysLoginLog);
    }
}

4.1.4、导入获取ip地址的工具类 IpUtil

在这里插入图片描述

package com.gansu.system.utils;

import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 获取ip地址
 */
public class IpUtil {

    public static String getIpAddress(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        // ipAddress = this.getRequest().getRemoteAddr();

        return ipAddress;
    }

    public static String getGatwayIpAddress(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        return ip;
    }
}

4.1.5、在 TokenLoginFilter 调用方法实现

在这里插入图片描述

package com.gansu.system.fillter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gansu.common.result.Result;
import com.gansu.common.result.ResultCodeEnum;
import com.gansu.common.utils.JwtHelperUtils;
import com.gansu.common.utils.ResponseUtil;
import com.gansu.model.vo.LoginVo;
import com.gansu.system.custom.CustomUser;
import com.gansu.system.service.AsyncLoginLogService;
import com.gansu.system.utils.IpUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

//4.自定义认证过滤器
 //登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {

    private RedisTemplate redisTemplate;

    private AsyncLoginLogService asyncLoginLogService;

    //构造
    public TokenLoginFilter(AuthenticationManager authenticationManager,RedisTemplate redisTemplate,
                            AsyncLoginLogService asyncLoginLogService) {
        this.setAuthenticationManager(authenticationManager);
        this.setPostOnly(false);
        //指定登录接口及提交方式,可以指定任意路径
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/system/index/login","POST"));

        this.redisTemplate = redisTemplate;

        this.asyncLoginLogService = asyncLoginLogService;
    }

    //获取用户名和密码进行认证
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
            throws AuthenticationException {
        try {
            LoginVo loginVo = new ObjectMapper().readValue(req.getInputStream(), LoginVo.class);

            Authentication authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
            return this.getAuthenticationManager().authenticate(authenticationToken);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

   //认证成功
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
                                            Authentication auth) throws IOException, ServletException {
       //获取认证对象
        CustomUser customUser = (CustomUser) auth.getPrincipal();
        //生成token
        String token = JwtHelperUtils.createToken(
                customUser.getSysUser().getId(),
                customUser.getSysUser().getUsername());
        //保存权限数据
        redisTemplate.opsForValue().set(customUser.getUsername(),
                JSON.toJSONString(customUser.getAuthorities()));

        asyncLoginLogService.recordLoginLog(customUser.getUsername(),
                IpUtil.getIpAddress(request),1,"登录成功");
        //返回
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        ResponseUtil.out(response, Result.ok(map));
    }

    //认证失败
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException e) throws IOException, ServletException {

        if(e.getCause() instanceof RuntimeException) {
            ResponseUtil.out(response, Result.build(null, 204, e.getMessage()));
        } else {
            ResponseUtil.out(response, Result.build(null, ResultCodeEnum.LOGIN_MOBLE_ERROR));
        }
    }
}

4.1.6、修改 WebSecurityConfig

在这里插入图片描述

package com.gansu.system.config;

import com.gansu.system.custom.CustomMd5PasswordEncoder;
import com.gansu.system.fillter.TokenAuthenticationFilter;
import com.gansu.system.fillter.TokenLoginFilter;
import com.gansu.system.service.AsyncLoginLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity //@EnableWebSecurity是开启SpringSecurity的默认行为
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解功能,默认禁用注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private CustomMd5PasswordEncoder customMd5PasswordEncoder;
    @Autowired
    private AsyncLoginLogService asyncLoginLogService;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 这是配置的关键,决定哪些接口开启防护,哪些接口绕过防护
        http
                //关闭csrf
                .csrf().disable()
                // 开启跨域以便前端调用接口
                .cors().and()
                .authorizeRequests()
                // 指定某些接口不需要通过验证即可访问。登陆接口肯定是不需要认证的
                .antMatchers("/admin/system/index/login").permitAll()
                // 这里意思是其它所有接口需要认证才能访问
                .anyRequest().authenticated()
                .and()
                //TokenAuthenticationFilter放到UsernamePasswordAuthenticationFilter的前面,
                // 这样做就是为了除了登录的时候去查询数据库外,其他时候都用token进行认证。
                .addFilterBefore(new TokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class)
                .addFilter(new TokenLoginFilter(authenticationManager(),
                        redisTemplate,asyncLoginLogService));


        //禁用session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 指定UserDetailService和加密器
        auth.userDetailsService(userDetailsService).passwordEncoder(customMd5PasswordEncoder);
    }

    /**
     * 配置哪些请求不拦截
     * 排除swagger相关请求
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/favicon.ico","/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**", "/doc.html");
    }
}

4.1.7、测试

测试前数据库:

在这里插入图片描述

登录成功后数据库:

在这里插入图片描述

在这里插入图片描述

登录日志前端 实现方式参考菜单管理

4.2、操作日志实现

系统引入common-log模块,采用AOP及自定义标签实现操作日志记录

4.2.1、创建common-log 模块

在这里插入图片描述

在这里插入图片描述

引入依赖

在这里插入图片描述

<dependencies>
    <dependency>
        <groupId>com.gansu</groupId>
        <artifactId>model</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>com.gansu</groupId>
        <artifactId>common-util</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

4.2.2、创建自定义注解Log

在这里插入图片描述

package com.gansu.system.annotation;

import com.gansu.system.enums.BusinessType;
import com.gansu.system.enums.OperatorType;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义操作日志记录注解
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
    /**
     * 模块
     */
    public String title() default "";

    /**
     * 功能
     */
    public BusinessType businessType() default BusinessType.OTHER;

    /**
     * 操作人类别
     */
    public OperatorType operatorType() default OperatorType.MANAGE;

    /**
     * 是否保存请求的参数
     */
    public boolean isSaveRequestData() default true;

    /**
     * 是否保存响应的参数
     */
    public boolean isSaveResponseData() default true;
}

4.2.3、创建AOP类 LogAspect

在这里插入图片描述

4.2.4、创建枚举类

在这里插入图片描述

BusinessType

package com.gansu.system.enums;

/**
 * 业务操作类型
 */
public enum BusinessType {
    /**
     * 其它
     */
    OTHER,

    /**
     * 新增
     */
    INSERT,

    /**
     * 修改
     */
    UPDATE,

    /**
     * 删除
     */
    DELETE,

    /**
     * 授权
     */
    ASSGIN,

    /**
     * 导出
     */
    EXPORT,

    /**
     * 导入
     */
    IMPORT,

    /**
     * 强退
     */
    FORCE,

    /**
     * 更新状态
     */
    STATUS,

    /**
     * 清空数据
     */
    CLEAN,
}

OperatorType

package com.gansu.system.enums;

/**
 * 操作人类别
 */
public enum OperatorType {
    /**
     * 其它
     */
    OTHER,

    /**
     * 后台用户
     */
    MANAGE,

    /**
     * 手机端用户
     */
    MOBILE
}

4.2.3、创建AOP类LogAspect

在这里插入图片描述

package com.gansu.system.aspect;

import java.util.Collection;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;
import com.gansu.common.utils.IpUtil;
import com.gansu.common.utils.JwtHelperUtils;
import com.gansu.model.system.SysOperLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import com.gansu.system.annotation.Log;
import com.gansu.system.service.AsyncOperLogService;

/**
 * 操作日志记录处理
 */
@Aspect
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    //微服务切换为feign调用接口
    @Resource
    private AsyncOperLogService asyncOperLogService;

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
        try {
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            HttpServletRequest request = sra.getRequest();

            // *========数据库日志=========*//
            SysOperLog operLog = new SysOperLog();
            operLog.setStatus(1);
            // 请求的地址
            String ip = IpUtil.getIpAddress(request);//IpUtil.getIpAddr(ServletUtils.getRequest());
            operLog.setOperIp(ip);
            operLog.setOperUrl(request.getRequestURI());

            String token = request.getHeader("token");
            String userName = JwtHelperUtils.getUsername(token);
            operLog.setOperName(userName);

            if (e != null) {
                operLog.setStatus(0);
                operLog.setErrorMsg(e.getMessage());
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            operLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            operLog.setRequestMethod(request.getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
            // 保存数据库
           // asyncOperLogService.saveSysLog(operLog);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     *
     * @param log     日志
     * @param operLog 操作日志
     * @throws Exception
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception {
        // 设置action动作
        operLog.setBusinessType(log.businessType().name());
        // 设置标题
        operLog.setTitle(log.title());
        // 设置操作人类别
        operLog.setOperatorType(log.operatorType().name());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, operLog);
        }
        // 是否需要保存response,参数和值
        if (log.isSaveResponseData() && !StringUtils.isEmpty(jsonResult)) {
            operLog.setJsonResult(JSON.toJSONString(jsonResult));
        }
    }

    /**
     * 获取请求的参数,放到log中
     *
     * @param operLog 操作日志
     * @throws Exception 异常
     */
    private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog) throws Exception {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setOperParam(params);
        }
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (!StringUtils.isEmpty(o) && !isFilterObject(o)) {
                    try {
                        Object jsonObj = JSON.toJSON(o);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}

4.2.4、创建接口 AsyncOperLogService

在这里插入图片描述

package com.gansu.system.service;

import com.gansu.model.system.SysOperLog;

public interface AsyncOperLogService{

    public void saveSysLog(SysOperLog sysOperLog);

}

4.2.5、编写mapper

① 引入 common-logservice-util

在这里插入图片描述

<dependency>
    <groupId>com.gansu</groupId>
    <artifactId>common-log</artifactId>
    <version>1.0</version>
</dependency>

② 编写mapper

在这里插入图片描述

package com.gansu.system.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.gansu.model.system.SysOperLog;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Repository
@Mapper
public interface AsyncOperLogMapper extends BaseMapper<SysOperLog>{

    public void saveSysLog(SysOperLog sysOperLog);
}

4.2.6、编写接口实现

编写 AsyncOperLogService 接口实现 AsyncOperLogServiceImpl

在这里插入图片描述

package com.gansu.system.service.impl;

import com.gansu.model.system.SysOperLog;
import com.gansu.system.mapper.AsyncOperLogMapper;
import com.gansu.system.service.AsyncOperLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AsyncOperLogServiceImpl implements AsyncOperLogService {

    @Autowired
    private AsyncOperLogMapper asyncOperLogMapper;

    @Override
    public void saveSysLog(SysOperLog sysOperLog) {

            asyncOperLogMapper.saveSysLog(sysOperLog);
    }
}

4.2.7、进行简单测试

① 先清楚数据库之前的操作数据

在这里插入图片描述

② 进行一个角色的添加,例如:

在这里插入图片描述

 @Log(title = "添加角色操纵",businessType = BusinessType.INSERT)  //日志管理,进行添加的记录

在这里插入图片描述
数据库应该会增加一条:

在这里插入图片描述

在这里插入图片描述

五、项目部署

在这里插入图片描述

5.1 后端打包

① 打包步骤:

在这里插入图片描述

② 运行显示

在这里插入图片描述

5.2 前端打包

① 修改如下为 prod-api

在这里插入图片描述

② 进行打包,命令为 npm run build:prod

在这里插入图片描述

完成打包:

在这里插入图片描述

③ 将打包后的文件及其文件夹复制到解压后的nginx下面的html下面

在这里插入图片描述

④ 修改 nginx下面的nginx.conf 配置

在这里插入图片描述

location /prod-api/ {
       proxy_pass   http://localhost:8800/;
    }

⑤ 启动nginx nginx.exe

在这里插入图片描述

⑥ 直接浏览器访问: http://localhost/

在这里插入图片描述

接口测试文档也可以访问: http://localhost:8800/doc.html

在这里插入图片描述

nginx 关机 nginx.exe -s stop

在这里插入图片描述

文章源码

系统文档资料

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2090803.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Matlab】时间序列模型(ARIMA)

文章目录 前言一、示例二、代码实现----Matlab全部数据的平稳性检验ADF检验图检验法 划分训练集平稳性检验确定 p&#xff0c;q结果分析和模型检验模型预测 前言 接上一篇博客&#xff0c;用 Matlab 完成代码编写。 【学习笔记】时间序列模型(ARIMA) 一、示例 已知一个上市公…

Linux云计算 |【第二阶段】SHELL-DAY4

主要内容&#xff1a; grep、egrep的使用&#xff0c;基本/扩展正则、sed的使用&#xff08;非交互、逐行处理、常用命令与选项&#xff09; 补充&#xff1a;grep [-E]选项&#xff0c;表示允许使用扩展的正则表达式&#xff08;同等于egrep&#xff09; 一、grep 与 egrep 的…

Kubernetes高可用部署

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

极限的性质【上】《用Manim可视化》

通过前面的极限的定义&#xff0c;现在是计算极限的时候了。然而&#xff0c;在此之前&#xff0c;我们需要一些极限的性质&#xff0c;这将使我们的工作变得简单一些。我们先来看看这些。 极限的性质&#xff1a; 1.常数对极限的影响 1.首先&#xff0c;我们假设和存在&…

Tengine框架之配置表的Luban转换与加载

对于一个游戏来说&#xff0c;配置表是必不可少的&#xff0c;而且文件数量还比较多&#xff0c;像活动、任务成就、图鉴、地图、皮肤、本地化语言、技能等等之类。配置表一般使用Excel格式&#xff0c;便于策划来配置。但游戏中一般使用txt/json/xml/二进制格式文件&#xff0…

碎碎念之Android中CPU架构arm-v8a、arm-v7a、x86

0 碎碎念 之前写博客都是为了复习基础知识&#xff0c;不过好像也忘得很快hh。 以后估计会写点感兴趣的自己碎碎念&#xff0c;缓解下emo的心情。&#xff08;不像之前的博客&#xff0c;这些博客不准备复现也不贴代码所以不一定对&#xff0c; 仅供个人参考 &#xff09; 现在…

Python酷库之旅-第三方库Pandas(111)

目录 一、用法精讲 486、pandas.DataFrame.count方法 486-1、语法 486-2、参数 486-3、功能 486-4、返回值 486-5、说明 486-6、用法 486-6-1、数据准备 486-6-2、代码示例 486-6-3、结果输出 487、pandas.DataFrame.cov方法 487-1、语法 487-2、参数 487-3、功…

鸿蒙项目签名配置

配置需要以下四个文件&#xff1a; 1. p12文件 2. csr文件 3. cer文件 打开AGC平台 点击申请调试证书 4. p7b文件 最后在项目中进行配置 配置项目的module.json5中

selenium使用指南

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 概述 selenium是网页应用中最流行的自动化测试工具&#xff0c;可以用来做自动化测试或者浏览器爬虫等。官网地址为&#xff1a;相对于另外一款web自动化测试工…

Linux安装Hadoop(单机版)详细教程

目录 一、JDK安装 1、下载JDK安装包 2、解压下载的JDK安装包 3、移动并重命名JDK包 4、配置Java环境变量 5、验证安装是否成功 二、Hadoop安装 1、下载Hadoop安装包 2、解压Hadoop安装包 3、配置Hadoop环境变量 4、修改配置文件 5、验证Hadoop是否安装成功 三&…

C# 开发环境搭建(Avalonia UI、Blazor Web UI、Web API 应用示例)

文章目录 C# 文档.NET SDK 下载安装 .NET SDK安装完验证VS Code 配置C# 开发插件settings.json WPF UI 框架Avalonia UI 框架创建 Avalonia 项目Avalonia 官方示例Avalonia 桌面 UI 库 Blazor Web UI 框架创建应用项目结构 ASP.NET Core 控制器创建 Web APIdotnet 命令dotnet n…

探索Unity与C#的无限潜能:从新手到高手的编程之旅

在数字创意与技术创新交织的今天&#xff0c;Unity游戏引擎凭借其强大的跨平台能力和灵活的编程接口&#xff0c;成为了无数开发者心中的首选。而C#&#xff0c;作为Unity的官方脚本语言&#xff0c;更是以其面向对象的特性和丰富的库支持&#xff0c;为游戏开发注入了无限可能…

克隆技术在代码溯源和复用及变更分析中的应用

摘要&#xff1a; 克隆技术已在软件代码溯源分析领域得到广泛应用&#xff0c;而轩宇软件成分分析系统基于溯源分析和同源分析技术&#xff0c;通过先进的特征向量提取、相似性匹配、高效检索引擎等多项技术&#xff0c;帮助企业高效识别代码来源、评估自主可控率&#xff0c;…

【数据结构】关于哈希表内部原理,你到底了解多少???(超详解)

前言&#xff1a; &#x1f31f;&#x1f31f;本期讲解关于哈希表的内部实现原理&#xff0c;希望能帮到屏幕前的你。 &#x1f308;上期博客在这里&#xff1a;http://t.csdnimg.cn/7D225 &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 目录 &a…

安嘉空间:智慧科技守护空间健康

在当今社会&#xff0c;随着人们对生活质量要求的不断提升&#xff0c;室内环境的健康与安全问题日益受到重视。安嘉空间&#xff0c;作为一家致力于人居健康空间技术研发的高科技企业&#xff0c;以其独创的技术和卓越的产品&#xff0c;为广大用户提供了一套全面的空间健康解…

进销存自动统计单据表格——未来之窗行业应用跨平台架构

一、代码 function 未来之窗_人工智能_计算单据(objbyclass,obj目标显示,obj目标值){console.log("未来之窗_人工智能_计算单据"objbyclass);var 计算结果0;$("."objbyclass).each(function(){var 输入框值 $(this).val();console.log("输入框值"…

去中心化(Decentralization)

去中心化&#xff08;Decentralization) 并不是一个新概念&#xff0c;它已在战略、管理和政府中使用了很长时间。去中心化的基本思想是将控制权和权限分配给组织的外围&#xff0c;而不是由一个中心机构完全控制组织。这种配置为组织带来了许多好处&#xff0c;例如提高了效率…

欧洲用户对中国应用程序的感知:一个复杂的挂毯

在数字时代&#xff0c;中国应用程序迅速在全球范围内占有一席之地&#xff0c;吸引了全球用户的注意力和好奇心。在欧洲&#xff0c;这些应用程序引发了人们的兴趣、阴谋和担忧&#xff0c;不同国家和人口统计数据的看法差异很大。 对中国应用程序感兴趣的主要驱动力之一是它…

git命令使用详情

目录 一. 安装教程 二. git配置 1. 查看git配置参数 2. 设置邮箱和用户名 3. SSH配置 4. 配置git远程库公钥 5. 编码设置 三. git 提交流程 1. 整体操作流程图 2. Git仓库包含5个区域 3. 下载、提交、更新命令 3.1. 下载 3.2. 提交 3.3. 更新&#xff08;两种方式…

无人机 PX4 飞控 | ROS应用层开发:基础代码框架构建

无人机 PX4 飞控 | ROS应用层开发&#xff1a;基础代码框架构建 基础代码框架构建文件建立代码基本构建测试 基础代码框架构建 本篇博客拟在构建一个 无人机 PX4 飞控 ROS应用层开发 的 基础代码框架。 其中包含了基础类文件、类头文件、main主函数文件&#xff0c;及其编译所…