SpringBoot+Vue集成AOP系统日志

news2024/7/6 18:56:25

新建logs表

添加aop依赖

<!--        aop依赖-->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

 新建获取ip地址工具类

import javax.servlet.http.HttpServletRequest;

/**
 * 功能:IpUtils
 * 获取ip地址工具类
 * 作者:爱因斯坦乐
 */
public class IpUtils {
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown ".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown ".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown ".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown ".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown ".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

}

新建注解@HoneyLogs

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HoneyLogs {
    //操作的模块
    String operation();

    //操作类型
    LogType type();
}

新建枚举类

/**
 * 系统日志的操作类型的枚举
 */
public enum LogType {
    ADD("新增"), UPDATE("更新"), DELETE("删除"), LOGIN("登录");
    private String value;

    public String getValue() {
        return value;
    }

    LogType(String value) {
        this.value = value;
    }
}

 创建日志实体类

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 功能:Logs
 * 日志实体类
 * 作者:爱因斯坦乐
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Logs {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String operation;
    private String type;
    private String ip;
    private String user;
    private String time;
}

 LogsMapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.shieldspring.entity.Logs;

public interface LogsMapper extends BaseMapper<Logs> {
}

LogsService

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.shieldspring.entity.Logs;
import com.example.shieldspring.mapper.LogsMapper;
import org.springframework.stereotype.Service;

/**
 * 功能:LogsService
 * 作者:爱因斯坦乐
 */
@Service
public class LogsService extends ServiceImpl<LogsMapper, Logs> {
}

Logs接口

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.shieldspring.common.Result;
import com.example.shieldspring.entity.Logs;
import com.example.shieldspring.service.LogsService;
import com.example.shieldspring.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 功能:LogsController
 * 作者:爱因斯坦乐
 * 日期:2024/6/30
 */
@RestController
@RequestMapping("/logs")
public class LogsController {
    @Autowired
    LogsService logsService;
    @Autowired
    UserService userService;

    //    删除
    @DeleteMapping("/delete/{id}")
    public Result delete(@PathVariable Integer id) {
        logsService.removeById(id);
        return Result.success();
    }

    //    批量删除
    @DeleteMapping("/delete/batch")
    public Result batchDelete(@RequestBody List<Integer> ids) {
        logsService.removeBatchByIds(ids);
        return Result.success();
    }

    //查询全部信息
    @GetMapping("/selectAll")
    public Result selectAll() {
        List<Logs> logsList = logsService.list(new QueryWrapper<Logs>().orderByDesc("id"));
        return Result.success(logsList);
    }

    //分页查询
    @GetMapping("/selectByPage")
    public Result selectByPage(@RequestParam Integer pageNum, @RequestParam Integer pageSize, @RequestParam String operation) {
        QueryWrapper<Logs> queryWrapper = new QueryWrapper<Logs>().orderByDesc("id");
        queryWrapper.like(StrUtil.isNotBlank(operation), "operation", operation);
        Page<Logs> page = logsService.page(new Page<>(pageNum, pageSize), queryWrapper);
        return Result.success(page);
    }
}

切面LogsAspect

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import com.example.shieldspring.common.HoneyLogs;
import com.example.shieldspring.entity.Login;
import com.example.shieldspring.entity.Logs;
import com.example.shieldspring.service.LogsService;
import com.example.shieldspring.utils.IpUtils;
import com.example.shieldspring.utils.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

/**
 * 功能:LogsAspect
 * 切面
 * 作者:爱因斯坦乐
 * 日期:2024/6/30
 */
@Component
@Aspect
@Slf4j
public class LogsAspect {
    @Resource
    LogsService logsService;

    @AfterReturning(pointcut = "@annotation(honeyLogs)", returning = "jsonResult")
    public void recordLog(JoinPoint joinPoint, HoneyLogs honeyLogs, Object jsonResult) {
        //获取当前登陆的用户信息
        Login login = TokenUtils.getCurrentLogin();
        if (login == null) {
            //null时从参数里获取登录信息  登录
            Object[] args = joinPoint.getArgs();
            if (ArrayUtil.isNotEmpty(args)) {
                if (args[0] instanceof Login) {
                    login = (Login) args[0];
                }
            }
        }
        if (login == null) {
            log.error("记录日志报错,为获取到当前操作用户信息");
            return;
        }
        //获取HttpServletRequest对象
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest requset = servletRequestAttributes.getRequest();
        //获取IP信息
        String ipAddr = IpUtils.getIpAddr(requset);
        //组装日志的实体对象
        Logs logs = Logs.builder().user(login.getName()).operation(honeyLogs.operation())
                .type(honeyLogs.type().getValue()).ip(ipAddr).time(DateUtil.now()).build();
        //插入数据库
        ThreadUtil.execAsync(() -> {
            try {
                logsService.save(logs);
            }catch (Exception e){
                log.error("存日志失败");
            }
        });
    }
}

前端实现logs.vue

<template>
  <div>
    <div>
      <!-- 查询条件 -->
      <el-input style="margin-right: 40px;width:200px" placeholder="查询标题" v-model="operation"></el-input>
      <el-button type="primary" @click="load(1)">查询</el-button>
      <el-button @click="reset" style="margin-right: 40px;">重置</el-button>
    </div>
    <!-- 操作 -->
    <div style="margin: 10px 0;">
      <el-button type="danger" plain @click="delBatch">批量删除</el-button>
    </div>
    <el-table
      :data="logs"
      :header-cell-style="{backgroundColor:'aliceblue',color:'#666'}"
      @selection-change="handleSelectionChange"
    >
      <el-table-column type="selection" width="55" align="center"></el-table-column>
      <el-table-column prop="id" label="序号"></el-table-column>
      <el-table-column prop="operation" label="操作模块"></el-table-column>
      <el-table-column prop="type" label="操作类型">
        <template v-slot="scope">
          <el-tag type="primary" v-if="scope.row.type==='新增'">{{scope.row.type}}</el-tag>
          <el-tag type="info" v-if="scope.row.type==='更新'">{{scope.row.type}}</el-tag>
          <el-tag type="danger" v-if="scope.row.type==='删除'">{{scope.row.type}}</el-tag>
          <el-tag type="danger" v-if="scope.row.type==='批量删除'">{{scope.row.type}}</el-tag>
          <el-tag type="success" v-if="scope.row.type==='登录'">{{scope.row.type}}</el-tag>
          <el-tag type="success" v-if="scope.row.type==='导出'">{{scope.row.type}}</el-tag>
          <el-tag type="success" v-if="scope.row.type==='导入'">{{scope.row.type}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="ip" label="操作人IP" align="center"></el-table-column>
      <el-table-column prop="user" label="操作人" align="center"></el-table-column>
      <el-table-column prop="time" label="发布时间" align="center"></el-table-column>
      <el-table-column label="操作" align="center" width="200px" fixed="right">
        <template v-slot="scope" width="180">
          <el-button size="mini" type="danger" plain @click="del(scope.row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <div style="margin: 10px 0;">
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[5, 10, 15, 20]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
        align="center"
      ></el-pagination>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      isSwitchClicked: false,
      logs: [],
      pageNum: 1,//当前页码
      pageSize: 5,//每页个数
      operation: '',
      total: 0,
      FormVisible: false,
      form: {},//新增表单数据
      user: JSON.parse(localStorage.getItem('hon-admin') || '{}'),
      ids: []
    }
  },
  created () { this.load() },
  methods: {
    //分页查询
    load (pageNum) {
      if (pageNum) {
        this.pageNum = pageNum
      }
      this.$request.get('/logs/selectByPage', {
        params: {
          pageNum: this.pageNum,
          pageSize: this.pageSize,
          operation: this.operation,
        }
      }).then(res => {
        this.logs = res.date.records
        // console.log(this.spys)
        this.total = res.date.total
      })
    },
    // 分页方法
    handleSizeChange (pageSize) {
      this.pageSize = pageSize
      this.load()
    },
    handleCurrentChange (pageNum) {
      this.load(pageNum)
    },
    //重置
    reset () {
      this.operation = ''
      this.load()
    },
    //删除按钮
    del (id) {
      this.isSwitchClicked = true
      this.$confirm('您确认删除吗?', '确认删除', { type: "warning" }).then(response => {
        this.$request.delete('logs/delete/' + id).then(res => {
          if (res.code === '200') {
            this.$message.success('删除成功')
            this.load(1)
          } else {
            this.$message.error(res.msg)
          }
        })
      }).catch(() => {
        // 用户点击了取消按钮,什么也不做
      })
    },
    //批量删除
    delBatch () {
      if (!this.ids.length) {
        this.$message.warning('请选择数据')
        return
      }
      this.$confirm('您确认批量删除这些数据吗?', '确认删除', { type: "warning" }).then(response => {
        this.$request.delete('logs/delete/batch', { data: this.ids }).then(res => {
          if (res.code === '200') {
            this.$message.success('删除成功')
            this.load(1)
          } else {
            this.$message.error(res.msg)
          }
        })
      }).catch(() => {
        // 用户点击了取消按钮,什么也不做
      })
    },
    handleSelectionChange (rows) {
      this.ids = rows.map(v => v.id)
    }
  }
}
</script>
<style>
.el-tooltip__popper {
  max-width: 400px !important;
}
</style>

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

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

相关文章

java常用类(3)

目录 一. 正则表达式 二. Math类 三. Random类 四. Date类 五. Calendar类 六. SimpDateFormate类 七. BigInteger类 八. BigDecimal类 一. 正则表达式 正则表达式(Regular Expression)就是用一些特殊的符号去匹配一个字符串是否符合规则,利用String类中的matches()方…

离线查询+线段树,CF522D - Closest Equals

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 522D - Closest Equals 二、解题报告 1、思路分析 考虑查询区间已经给出&#xff0c;我们可以离线查询 对于这类区间离线查询的问题我们通常可以通过左端点排序&#xff0c;然后遍历询问同时维护左区间信息…

用机器改变人类方向

1800 世纪初&#xff0c;美国迎来了工业革命&#xff0c;这是一个由技术进步推动的变革时代。新机器和制造技术的引入重塑了经济格局&#xff0c;提高了生产效率&#xff0c;同时减少了某些领域对手工劳动的需求。因此&#xff0c;这种转变导致了失业。 如今&#xff0c;我们看…

【漏洞复现】朗新智能人力资源系统(HCM) GetFunc_code.asmx接口处存在SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

产线AGV和仓储AGV到底有什么不同?

agv AGV小车虽然体积小巧&#xff0c;但这并不影响它强大的负重能力&#xff0c;它不需要人工去操作驾驶&#xff0c;能够实现无人搬运车的功能&#xff0c;而且随着AGV小车的发展&#xff0c;已经从最传统普遍的磁导航升级为惯性导引和激光导引AGV小车了&#xff0c;从需要在企…

2. Python+Playwright playwright的API

Playwright支持同步和异步两种API&#xff0c;使用异步API需要导入asyncio库&#xff0c;它是一个可以用来实现Python协程的库&#xff0c;更详细介绍可参考Python协程 。我们可以根据自己的偏好选择适合的模式。 同步与异步模式原理 同步操作方式&#xff1a;在代码执行时&am…

【目标检测】DINO

一、引言 论文&#xff1a; DINO: DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection 作者&#xff1a; IDEA 代码&#xff1a; DINO 注意&#xff1a; 该算法是在Deformable DETR、DAB-DETR、DN-DETR基础上的改进&#xff0c;在学习该算法前&#…

黑马点评-Redis的缓存击穿,缓存雪崩,缓存穿透,互斥锁,逻辑过期

文章目录 1.缓存穿透2.缓存雪崩3.缓存击穿3.1 互斥锁3.2 基于逻辑过期 1.缓存穿透 解决办法 写入NULL值到Redis缓存&#xff0c;以后就会命中Redis的控制缓存而不会出现请求直接打到数据库的问题&#xff01; 代码 2.缓存雪崩 这个概念很好理解&#xff0c;雪崩就是无数的…

复旦大学:一个小技巧探测大模型的知识边界,有效消除幻觉

孔子说“知之为知之&#xff0c;不知为不知&#xff0c;是知也”&#xff0c;目前的大模型非常缺乏这个能力。虽然大模型拥有丰富的知识&#xff0c;但它仍然缺乏对自己知识储备的正确判断。近年来LLMs虽然展现了强大的能力&#xff0c;但它们偶尔产生的内容捏造&#xff0c;即…

240703_昇思学习打卡-Day15-K近邻算法实现红酒聚类

KNN(K近邻)算法实现红酒聚类 K近邻算法&#xff0c;是有监督学习中的分类算法&#xff0c;可以用于分类和回归&#xff0c;本篇主要讲解其在分类上的用途。 文章目录 KNN(K近邻)算法实现红酒聚类算法原理数据下载数据读取与处理模型构建--计算距离模型预测 算法原理 KNN算法虽…

AIGC到底如何改变创意设计?

在当今数字化时代&#xff0c;AIGC&#xff08;生成式人工智能&#xff09;技术的崛起对创意设计领域产生了深远的影响。AIGC不仅为设计师提供了新的工具和方法&#xff0c;还改变了传统的设计流程和思维方式。 传统的设计过程中&#xff0c;设计师需要耗费大量时间在绘图、修…

利用GPT 将 matlab 内置 bwlookup 函数转C

最近业务需要将 matlab中bwlookup 的转C 这个函数没有现成的m文件参考&#xff0c;内置已经打成库了&#xff0c;所以没有参考源代码 但是它的解释还是很清楚的&#xff0c;可以根据这个来写 Nonlinear filtering using lookup tables - MATLAB bwlookup - MathWorks 中国 A…

甘肃黄米粽子:香甜软糯的塞上美食

甘肃黄米粽子是甘肃地区具有特色的传统美食。黄米粽子选用优质的黄米作为主要原料&#xff0c;黄米相较于糯米&#xff0c;有着独特的谷物香气和口感。在制作过程中&#xff0c;将黄米浸泡一段时间&#xff0c;使其充分吸收水分&#xff0c;变得饱满。馅料方面&#xff0c;通常…

Vue 爬坑

都是基于最新的Vue3版本 "vue": "^3.4.29" 1 vue组建样式设置 <script setup lang"ts"> import HelloWorld from ./components/HelloWorld.vue </script><template><div><a href"https://vitejs.dev" tar…

鸿翼打造企业级AI Agent智能体平台,构建AI +ECM全业务场景

在数字化时代的浪潮中&#xff0c;人工智能技术正以前所未有的速度改变着世界。正如比尔盖茨预言&#xff0c;AI Agent将是人工智能的未来。在这个预言逐渐成为现实的当下&#xff0c;大模型驱动的智能体正在成为推动企业革新的核心动力。 在企业环境中&#xff0c;大语言模型的…

DEX: Scalable Range Indexing on Disaggregated Memory——论文泛读

arXiv Paper 论文阅读笔记整理 问题 内存优化索引[2&#xff0c;3&#xff0c;18&#xff0c;27&#xff0c;42]对于加速OLTP至关重要&#xff0c;但随着数据大小&#xff08;以及索引大小&#xff09;的增长&#xff0c;对内存容量的需求可能会超过单个服务器所能提供的容量…

华为手机改变休眠时间 不让手机动不动黑屏

在手机中找到设置 并打开 在里面找到显示与亮度 并点开 找到并点击休眠操作项 然后就会弹出 多久进入休眠 可以调久一点

机器学习基础概念

1.机器学习定义 2.机器学习工作流程 &#xff08;1&#xff09;数据集 ①一行数据&#xff1a;一个样本 ②一列数据&#xff1a;一个特征 ③目标值&#xff08;标签值&#xff09;&#xff1a;有些数据集有目标值&#xff0c;有些数据集没有。因此数据类型由特征值目标值构成或…

vmware虚拟机增加磁盘容量

概述 当初始分配给虚拟机的磁盘空间不够时&#xff0c;需要从外部的主系统增加配给。 具体操作分为两步&#xff1a;一&#xff1a;通过虚拟机界面添加分配的磁盘配给&#xff1b;二&#xff1a;将新分配的配给给使用起来。 操作 添加磁盘配给 在虚拟机内部添加新分配的配给…

Linux下QT程序启动失败问题排查方法

文章目录 0.问题背景1.程序启动失败常见原因2.排查依赖库问题2.1 依赖库缺失2.2 依赖库加载路径错误2.3 依赖库版本不匹配2.4 QT插件库缺失2.4.1 QT插件库缺失2.4.2 插件库自身的依赖库缺失 2.5 系统基础C库不匹配 3.资源问题3.1 缺少翻译文件3.2 缺少依赖的资源文件3.3 缺少依…