还是接着上一个的内容,我把这个新增的关于日志的功能代码都贴出来,供大家学习参考。数据库
数据库
drop table if exists an_log;
create table an_log(
id int not null auto_increment primary key comment '主键id',
name varchar(255) not null comment '操作内容',
log_date datetime default CURRENT_TIMESTAMP comment '日志时间',
user_name varchar(100) default null comment '操作人',
`ip` varchar(100) default null comment 'ip地址'
) comment '操作日志表';
insert into an_log values(1,'操作','2022-01-01','admin','192.168.0.1');
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
application.proerties
server.port=8089
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=UTC
spring.datasource.username=root
spring.datasource.password=mysql123
#格式化时间
spring.jackson.date-format= yyyy-MM-dd
spring.jackson.time-zone= GMT+8
#日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#配置别名
mybatis-plus.type-aliases-package=com.shrimpking.pojo
#开启逻辑删除,标识字段
mybatis-plus.global-config.db-config.logic-delete-field=is_deleted
#删除
mybatis-plus.global-config.db-config.logic-delete-value=1
#未删除
mybatis-plus.global-config.db-config.logic-not-delete-value=0
#swagger
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
pojo
log.java
package com.shrimpking.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.io.Serializable;
import java.util.Date;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
* 操作日志表
* </p>
*
* @author shrimpking
* @since 2023-11-15
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("an_log")
@ApiModel(value="Log对象", description="操作日志表")
@AllArgsConstructor
public class Log implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键id")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "操作内容")
private String name;
@ApiModelProperty(value = "日志时间")
private LocalDateTime logDate;
@ApiModelProperty(value = "操作人")
private String userName;
@ApiModelProperty(value = "ip地址")
private String ip;
}
mapper
logmapper.java
package com.shrimpking.mapper;
import com.shrimpking.pojo.Log;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* <p>
* 操作日志表 Mapper 接口
* </p>
*
* @author shrimpking
* @since 2023-11-15
*/
public interface LogMapper extends BaseMapper<Log> {
}
mapper.xml
logMpper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.shrimpking.mapper.LogMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.shrimpking.pojo.Log">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="log_date" property="logDate" />
<result column="user_name" property="userName" />
<result column="ip" property="ip" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, name, log_date, user_name, ip
</sql>
</mapper>
service
logservice.java
package com.shrimpking.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.pojo.Log;
import com.baomidou.mybatisplus.extension.service.IService;
import com.shrimpking.req.LogParams;
import com.shrimpking.req.QueryParams;
/**
* <p>
* 操作日志表 服务类
* </p>
*
* @author shrimpking
* @since 2023-11-15
*/
public interface LogService extends IService<Log> {
IPage<Log> findBySearchPage(LogParams logParams);
}
serviceImpl
logServiceImpl.java
package com.shrimpking.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shrimpking.pojo.Hotel;
import com.shrimpking.pojo.Log;
import com.shrimpking.mapper.LogMapper;
import com.shrimpking.req.LogParams;
import com.shrimpking.req.QueryParams;
import com.shrimpking.service.LogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 操作日志表 服务实现类
* </p>
*
* @author shrimpking
* @since 2023-11-15
*/
@Service
public class LogServiceImpl extends ServiceImpl<LogMapper, Log> implements LogService {
@Override
public IPage<Log> findBySearchPage(LogParams logParams)
{
//声明分页
IPage<Log> page = new Page<>(logParams.getCurrentPage(), logParams.getPageSize());
//声明查询条件
LambdaQueryWrapper<Log> queryWrapper = new LambdaQueryWrapper<>();
//操作内容不为空,有条件值时,加入此条件
queryWrapper.like(
StringUtils.isNotBlank(logParams.getName()),
Log::getName, logParams.getName())
//操作人不为空时,加入条件
.like(
StringUtils.isNotBlank(logParams.getUserName()),
Log::getUserName, logParams.getUserName())
.orderByDesc(Log::getId);
//返回结果
return this.baseMapper.selectPage(page, queryWrapper);
}
}
controller
logController.java
package com.shrimpking.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shrimpking.annotation.AutoLog;
import com.shrimpking.pojo.Log;
import com.shrimpking.req.LogParams;
import com.shrimpking.res.Result;
import com.shrimpking.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 操作日志表 前端控制器
* </p>
*
* @author shrimpking
* @since 2023-11-15
*/
@RestController
@RequestMapping("/log")
public class LogController {
@Autowired
private LogService logService;
/**
* 有查询条件时,获取分页数据
* @param queryParams
* @return
*/
@GetMapping("/searchPage")
public Result findBySearchPage(LogParams logParams){
IPage<Log> list = this.logService.findBySearchPage(logParams);
return Result.success(list);
}
// @PostMapping("/save")
// public Result save(@RequestBody Log log){
// boolean save = this.logService.save(log);
// if(!save) return Result.error("保存失败");
// return Result.success("保存成功");
// }
//
// @PostMapping("/update")
// public Result update(@RequestBody Log log){
// boolean save = this.logService.updateById(log);
// if(!save) return Result.error("更新失败");
// return Result.success("更新成功");
// }
@DeleteMapping("/delete")
public Result delete(@RequestParam("id") Integer id){
boolean remove = this.logService.removeById(id);
if(!remove) return Result.error("删除失败");
return Result.success("删除成功");
}
@PostMapping("/delBatch")
public Result delBatch(@RequestBody List<Log> list){
//循环取出id
List<Integer> collectList = list.stream().map(Log::getId).collect(Collectors.toList());
boolean remove = this.logService.removeByIds(collectList);
if(!remove) return Result.error("删除失败");
return Result.success("删除成功");
}
}
req
logParams.java
package com.shrimpking.req;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/11/12 21:10
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class LogParams extends QueryParams
{
private String userName;
}
router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/login',
name:'LoginView',
component: ()=> import('@/views/LoginView.vue'),
},
{
path:'/register',
name: 'Register',
component: ()=> import('@/views/RegisterView.vue'),
},
{
path: '/',
redirect: '/home',
name: 'Layout',
component: ()=> import('@/views/Layout.vue'),
children:[
{
path: 'home',
name: 'HomeView',
component: ()=> import('@/views/HomeView.vue')
},
{
path: 'admin',
name: 'AdminView',
component: ()=> import('@/views/User/AdminView.vue'),
},
{
path:'user',
name:'UserView',
component: ()=> import('@/views/User/UserView.vue'),
},
{
path:'book',
name:'BookView',
component: ()=> import('@/views/Info/BookView.vue'),
},
{
path:'type',
name:'BookType',
component: ()=> import('@/views/Info/BookType.vue'),
},
{
path:'audit',
name:'AuditView',
component: ()=> import('@/views/AuditView.vue'),
},
{
path:'hotel',
name:'HotelView',
component: ()=> import('@/views/hotel/HotelView.vue'),
},
{
path:'reserve',
name:'ReserveView',
component: ()=> import('@/views/hotel/ReserveView.vue'),
},
{
path:'log',
name:'LogView',
component: ()=> import('@/views/Log/LogView.vue'),
},
]
},
]
const router = new VueRouter({
routes
})
//白名单
const IGNORE_URLS = ['/login','/register'];
//前置守卫
router.beforeEach((to, from, next) => {
//在白名单中,放行
if(IGNORE_URLS.includes(to.path)){
next();
}
//获取用户
let admin = JSON.parse(window.localStorage.getItem('access-admin'));
if(!admin && !IGNORE_URLS.includes(to.path)){
//没有登录 ,没有在白名单中,跳转登录
return next('/login');
}
next();
});
export default router
logview.vue
<template>
<div>
<!-- 搜索区域 -->
<div style="margin-bottom:15px;">
<el-input
v-model="searchForm.name"
style="width:200px;"
placeholder="请输入内容"
@clear="doSearch"
@keypress.native.enter="doSearch"
clearable>
</el-input>
<el-input
v-model="searchForm.userName"
style="width:200px;margin-left:10px;"
placeholder="请输入操作人"
@clear="doSearch"
@keypress.native.enter="doSearch"
clearable>
</el-input>
<el-button
type="warning"
style="margin-left: 10px;"
icon="el-icon-search"
@click="doSearch">查询</el-button>
<el-button
type="primary"
style="margin-left: 10px;"
icon="el-icon-toilet-paper"
@click="clearSearch">清空</el-button>
<!-- <el-button-->
<!-- type="primary"-->
<!-- style="margin-left: 10px;"-->
<!-- icon="el-icon-plus" @click="addBtn">新增</el-button>-->
<el-button
type="danger"
style="margin-left: 10px;"
icon="el-icon-delete"
@click="delBatchBtn"
v-if="admin.role === 'ROLE_ADMIN'">批量删除</el-button>
</div>
<!-- 表格区域 -->
<el-table
ref="table1"
:data="tableData"
border
row-key="id"
tooltip-effect="dark"
style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
:reserve-selection="true"
width="55">
</el-table-column>
<el-table-column
prop="id"
label="ID">
</el-table-column>
<el-table-column
prop="name"
label="操作内容">
</el-table-column>
<el-table-column
prop="logDate"
label="日志时间">
</el-table-column>
<el-table-column
prop="userName"
label="操作人">
</el-table-column>
<el-table-column
prop="ip"
label="IP地址">
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<!-- <el-button type="primary" icon="el-icon-edit" @click="editBtn(scope.row)">编辑</el-button>-->
<el-button
type="danger"
icon="el-icon-delete"
@click="deleteBtn(scope.row)"
v-if="admin.role === 'ROLE_ADMIN'">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页区域 -->
<div style="margin-top:15px;">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="searchForm.currentPage"
:page-sizes="[2, 5, 10, 20]"
:page-size="searchForm.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
<!-- 对话框 -->
<!-- <div>-->
<!-- <el-dialog-->
<!-- :title="dialogTitle"-->
<!-- :visible.sync="dialogFormVisible"-->
<!-- :close-on-click-modal="false"-->
<!-- @close="closeDialog"-->
<!-- width="35%">-->
<!-- <el-form-->
<!-- :model="addForm"-->
<!-- :rules="rules"-->
<!-- ref="addForm"-->
<!-- :label-width="formLabelWidth"-->
<!-- label-position="left">-->
<!-- <el-form-item label="姓名" prop="name">-->
<!-- <el-input v-model="addForm.name" clearable></el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="密码" prop="password">-->
<!-- <el-input v-model="addForm.password" clearable></el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="年龄" prop="age">-->
<!-- <el-input-number v-model="addForm.age" :max="199" :min="1" label="描述文字"></el-input-number>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="性别" prop="sex">-->
<!-- <el-radio v-model="addForm.sex" label="男">男</el-radio>-->
<!-- <el-radio v-model="addForm.sex" label="女">女</el-radio>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="电话" prop="phone">-->
<!-- <el-input v-model="addForm.phone" clearable></el-input>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="角色名" prop="role">-->
<!-- <el-select v-model="addForm.role" clearable>-->
<!-- <el-option label="教师" value="ROLE_TEACHER"></el-option>-->
<!-- <el-option label="学生" value="ROLE_STUDENT"></el-option>-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- </el-form>-->
<!-- <div slot="footer" class="dialog-footer">-->
<!-- <el-button @click="resetBtn" v-show="dialogTitle === '新增用户'">重 置</el-button>-->
<!-- <el-button type="primary" @click="submitBtn">确 定</el-button>-->
<!-- </div>-->
<!-- </el-dialog>-->
<!-- </div>-->
</div>
</template>
<script>
import request from "@/utils/request";
export default {
name: "LogView",
computed: {
admin(){
return JSON.parse(window.localStorage.getItem('access-admin')) || { name: '未登录'};
}
},
data() {
return {
//多选
multipleSelection:[],
//添加表单
// addForm:{
// name:'',
// password:'',
// age:'',
// sex:'',
// phone:'',
// role: '',
// },
// rules:{
// name:[{required: true, message: '请输入姓名', trigger: 'blur'}],
// password:[{required: true, message: '请输入密码', trigger: 'blur'}],
// age:[{required: true, message: '请输入年龄', trigger: 'blur'}],
// sex:[{required: true, message: '请选择性别', trigger: 'blur'}],
// phone:[{required: true, message: '请输入电话', trigger: 'blur'}],
// role:[{required: true, message: '请选择角色名', trigger: 'change'}],
// },
// //表单标题宽度
// formLabelWidth:'80px',
// //对话框标题
// dialogTitle:'',
// //对话框
// dialogFormVisible: false,
//搜索条件
searchForm:{
name: '',
userName:'',
currentPage: 1,
pageSize: 5
},
tableData: [],
total:0
}
},
methods: {
//批量删除
delBatchBtn(){
if(this.multipleSelection.length <= 0){
this.$message.warning("请先勾选数据,再删除!");
return;
}
this.$confirm(`您确定要删除这些数据吗`,'删除提示',{
confirmButtonText:'删除',
cancelButtonText:'取消',
type:'warning',
}).then(()=>{
//todo
request.post('/log/delBatch',this.multipleSelection)
.then(res => {
if(res.code === '200'){
this.$message.success(res.data);
this.doSearch();
}else {
this.$message.error(res.msg);
this.doSearch();
}
})
}).catch(_=>{
this.$message.warning('已取消删除');
})
},
//多选变化
handleSelectionChange(val) {
this.multipleSelection = val;
},
//删除
deleteBtn(row){
this.$confirm(`您确定要删除【${row.name}】吗`,'删除提示',{
confirmButtonText:'删除',
cancelButtonText:'取消',
type:'warning',
}).then(()=>{
request.delete('/log/delete',{
params:{ id : row.id}
}).then(res => {
if(res.code === '200'){
this.$message.success(res.data);
this.doSearch();
}
})
}).catch(_=>{
this.$message.warning('已取消删除');
})
},
//编辑
// editBtn(row){
// let obj = JSON.parse(JSON.stringify(row));
// this.addForm = obj;
// this.dialogTitle = "编辑用户";
// this.dialogFormVisible = true;
// },
//关闭对话框
// closeDialog(){
// this.resetBtn();
// this.dialogFormVisible = false;
// },
//新增保存
// submitBtn(){
// this.$refs.addForm.validate((valid)=>{
// if(valid){
// //校验通过
// //有id,编辑,没有id是新增
// request.post(this.addForm.id ? '/user/update':'/user/save',this.addForm)
// .then(res=>{
// if(res.code === '200'){
// this.$message.success(res.data);
// this.resetBtn();
// this.dialogFormVisible = false;
// this.doSearch();
// }else {
// this.$message.error(res.msg);
// }
// })
// }
// })
// },
//新增重置
// resetBtn(){
// this.$refs.addForm.resetFields();
// //修复bug
// this.addForm = {};
// //this.$refs.addForm.id = '';
// },
// addBtn(){
// this.dialogTitle = '新增用户';
// this.dialogFormVisible = true;
// },
clearSearch(){
this.searchForm.name = '';
this.searchForm.userName = '';
this.doSearch();
},
//搜索
doSearch(){
//修复bug
this.searchForm.currentPage = 1;
this.getData();
},
handleSizeChange(val) {
this.searchForm.pageSize = val;
this.searchForm.currentPage = 1;
this.getData();
},
handleCurrentChange(val) {
this.searchForm.currentPage = val;
this.getData();
},
//获取数据
getData(){
request.get('/log/searchPage',{
params: this.searchForm
}).then(res=>{
if(res.code === '200'){
this.tableData = res.data.records; //数据
this.searchForm.currentPage = res.data.current; //当前页
this.searchForm.pageSize = res.data.size; //页条数
this.total = res.data.total; //总条数
}else {
this.$message.error(res.msg);
}
});
}
},
created(){
//获取数据
this.getData();
}
}
</script>
<style scoped>
</style>
layout.vue
<template>
<div>
<el-container class="container">
<el-header class="header-area">
<img src="@/assets/logo.png" alt="logo" class="logo">
<span class="title">手牵手带小白做毕设</span>
<span class="admin-info">
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
用户: <strong>{{ admin.name }}</strong>
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</span>
</el-header>
<el-container class="middle-area">
<el-aside class="left-aside">
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
:unique-opened="true"
router>
<el-menu-item index="/home">
<i class="el-icon-menu"></i>
<span slot="title">系统首页</span>
</el-menu-item>
<el-submenu index="/admin" v-if="admin.role === 'ROLE_ADMIN'">
<template slot="title">
<i class="el-icon-location"></i>
<span>用户管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/admin">用户信息</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="/book">
<template slot="title">
<i class="el-icon-location"></i>
<span>信息管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/book">图书信息</el-menu-item>
<el-menu-item index="/type">图书分类</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="/audit">
<template slot="title">
<i class="el-icon-location"></i>
<span>请假管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/audit">请假申请</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="/hotel">
<template slot="title">
<i class="el-icon-location"></i>
<span>酒店管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/hotel">酒店信息</el-menu-item>
<el-menu-item index="/reserve">预订记录</el-menu-item>
</el-menu-item-group>
</el-submenu>
<el-submenu index="/log" v-if="admin.role === 'ROLE_ADMIN'">
<template slot="title">
<i class="el-icon-location"></i>
<span>日志管理</span>
</template>
<el-menu-item-group>
<el-menu-item index="/log">操作日志</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-aside>
<el-main>
<router-view/>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script>
import request from "@/utils/request";
export default {
name: "Layout",
computed: {
admin(){
return JSON.parse(window.localStorage.getItem('access-admin')) || { name: '未登录'};
}
},
methods: {
//下拉菜单命令
handleCommand(command){
if (command === 'logout') {
this.logout();
}
},
//退出
logout(){
request.post('/user/logout',this.admin).then(res => {
if(res.code === '200'){
window.localStorage.clear();
this.$message.success(res.data);
this.$router.replace('/login');
}
})
}
}
}
</script>
<style lang="scss" scoped>
.container{
height: 100vh;
.header-area{
background-color: #4c535a;
.logo {
width: 40px;
position: relative;
top: 10px;
}
.title{
font-size: 20px;
margin-left: 15px;
color: white;
}
.admin-info{
float: right;
margin-right: 30px;
line-height: 60px;
.el-dropdown-link{
color: #cccccc;
}
}
}
.middle-area{
.left-aside{
overflow: hidden;
height: 100%;
/*background-color: #545c64;*/
width:230px !important;
.el-menu-vertical-demo{
height: 100%;
}
}
}
}
</style>