[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-public-2havlmaC":3,"public-project-articles-2havlmaC":17},{"id":4,"uuid":5,"project_id":6,"title":7,"content":8,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},54,"2havlmaC",39,"21. SpringBoot+Vue集成AOP系统日志","\nlogs 表\n\n```sql\nCREATE TABLE `logs` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `operation` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作名称',\n  `type` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作类型',\n  `ip` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'ip地址',\n  `user` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作人',\n  `time` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '操作时间',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='系统日志';\n```\n\nAOP 扫盲：[https:\u002F\u002Fblog.csdn.net\u002Fqq_44693065\u002Farticle\u002Fdetails\u002F124327021](https:\u002F\u002Fblog.csdn.net\u002Fqq_44693065\u002Farticle\u002Fdetails\u002F124327021)\n\naop 依赖\n\n```java\n\u003C!--  aop  -->\n\u003Cdependency>\n    \u003CgroupId>org.springframework.boot\u003C\u002FgroupId>\n    \u003CartifactId>spring-boot-starter-aop\u003C\u002FartifactId>\n\u003C\u002Fdependency>\n```\n\n获取 IP 地址工具类 IpUtils.java\n\n```java\nimport javax.servlet.http.HttpServletRequest;\n\npublic class IpUtils {\n\n    public static String getIpAddr(HttpServletRequest request) {\n        String ip = request.getHeader(\"x-forwarded-for\");\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"X-Forwarded-For\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"WL-Proxy-Client-IP\");\n        }\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getHeader(\"X-Real-IP\");\n        }\n\n        if (ip == null || ip.length() == 0 || \"unknown\".equalsIgnoreCase(ip)) {\n            ip = request.getRemoteAddr();\n        }\n        return \"0:0:0:0:0:0:0:1\".equals(ip) ? \"127.0.0.1\" : ip;\n    }\n\n}\n```\n\n自定义注解 @HoneyLogs\n\n```java\nimport java.lang.annotation.*;\n\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface HoneyLogs {\n    \u002F\u002F 操作的模块\n    String operation();\n    \u002F\u002F 操作类型\n    String type();\n}\n```\n\n切面 LogAspect.java\n\n```java\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.thread.ThreadUtil;\nimport cn.hutool.core.util.ArrayUtil;\nimport com.example.springboot.common.HoneyLogs;\nimport com.example.springboot.entity.Logs;\nimport com.example.springboot.entity.User;\nimport com.example.springboot.service.LogsService;\nimport com.example.springboot.utils.IpUtils;\nimport com.example.springboot.utils.TokenUtils;\nimport lombok.extern.slf4j.Slf4j;\nimport org.aspectj.lang.JoinPoint;\nimport org.aspectj.lang.annotation.AfterReturning;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\n\n@Component\n@Aspect\n@Slf4j\npublic class LogsAspect {\n\n    @Resource\n    LogsService logsService;\n\n    @AfterReturning(pointcut = \"@annotation(honeyLogs)\", returning = \"jsonResult\")\n    public void recordLog(JoinPoint joinPoint, HoneyLogs honeyLogs, Object jsonResult) {\n        \u002F\u002F 获取当前登录的用户的信息\n        User loginUser = TokenUtils.getCurrentUser();\n        if (loginUser == null) { \u002F\u002F 用户未登录的情况下  loginUser是null  是null的话我们就要从参数里面获取操作人信息\n            \u002F\u002F 登录、注册\n            Object[] args = joinPoint.getArgs();\n            if (ArrayUtil.isNotEmpty(args)) {\n                if (args[0] instanceof User) {\n                    loginUser = (User) args[0];\n                }\n            }\n        }\n        if (loginUser == null) {\n            log.error(\"记录日志信息报错，未获取到当前操作用户信息\");\n            return;\n        }\n        \u002F\u002F 获取HttpServletRequest对象\n        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();\n        HttpServletRequest request = servletRequestAttributes.getRequest();\n\n        \u002F\u002F 获取到请求的ip\n        String ip = IpUtils.getIpAddr(request);\n        Logs logs = Logs.builder()\n                .operation(honeyLogs.operation())\n                .type(honeyLogs.type())\n                .ip(ip)\n                .user(loginUser.getUsername())\n                .time(DateUtil.now())\n                .build();\n\n        ThreadUtil.execAsync(() -> {\n            \u002F\u002F 异步记录日志信息\n            logsService.save(logs);\n        });\n    }\n\n}\n```\n\nLogs.java\n\n```java\nimport com.baomidou.mybatisplus.annotation.IdType;\nimport com.baomidou.mybatisplus.annotation.TableId;\nimport lombok.AllArgsConstructor;\nimport lombok.Builder;\nimport lombok.Data;\nimport lombok.NoArgsConstructor;\n\n@Data\n@AllArgsConstructor\n@NoArgsConstructor\n@Builder\npublic class Logs {\n    @TableId(type = IdType.AUTO)\n    private Integer id;\n    private String operation;\n    private String type;\n    private String ip;\n    private String user;\n    private String time;\n}\n```\n\nLogsController.java\n\n```java\nimport cn.hutool.core.util.StrUtil;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport com.example.springboot.common.Result;\nimport com.example.springboot.entity.Logs;\nimport com.example.springboot.service.LogsService;\nimport com.example.springboot.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n\u002F**\n * 系统日志相关接口\n *\u002F\n@RestController\n@RequestMapping(\"\u002Flogs\")\npublic class LogsController {\n\n    @Autowired\n    LogsService logsService;\n\n    @Autowired\n    UserService userService;\n\n\n    \u002F**\n     * 删除信息\n     *\u002F\n    @DeleteMapping(\"\u002Fdelete\u002F{id}\")\n    public Result delete(@PathVariable Integer id) {\n        logsService.removeById(id);\n        return Result.success();\n    }\n\n\n    \u002F**\n     * 批量删除信息\n     *\u002F\n    @DeleteMapping(\"\u002Fdelete\u002Fbatch\")\n    public Result batchDelete(@RequestBody List\u003CInteger> ids) {\n        logsService.removeBatchByIds(ids);\n        return Result.success();\n    }\n\n    \u002F**\n     * 多条件模糊查询信息\n     * pageNum 当前的页码\n     * pageSize 每页查询的个数\n     *\u002F\n    @GetMapping(\"\u002FselectByPage\")\n    public Result selectByPage(@RequestParam Integer pageNum,\n                               @RequestParam Integer pageSize,\n                               @RequestParam String operation) {\n        QueryWrapper\u003CLogs> queryWrapper = new QueryWrapper\u003CLogs>().orderByDesc(\"id\");  \u002F\u002F 默认倒序，让最新的数据在最上面\n        queryWrapper.like(StrUtil.isNotBlank(operation), \"operation\", operation);\n        Page\u003CLogs> page = logsService.page(new Page\u003C>(pageNum, pageSize), queryWrapper);\n        return Result.success(page);\n    }\n\n}\n```\n\nLogs.vue\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cdiv>\n      \u003Cel-input style=\"width: 200px\" placeholder=\"查询模块\" v-model=\"operation\">\u003C\u002Fel-input>\n      \u003Cel-select style=\"margin: 0 5px\" v-model=\"type\">\n        \u003Cel-option v-for=\"item in ['新增', '修改', '删除']\" :key=\"item\" :value=\"item\" :label=\"item\">\u003C\u002Fel-option>\n      \u003C\u002Fel-select>\n      \u003Cel-input style=\"width: 200px\" placeholder=\"查询操作人\" v-model=\"optUser\">\u003C\u002Fel-input>\n      \u003Cel-button type=\"primary\" style=\"margin-left: 10px\" @click=\"load(1)\">查询\u003C\u002Fel-button>\n      \u003Cel-button type=\"info\" @click=\"reset\">重置\u003C\u002Fel-button>\n    \u003C\u002Fdiv>\n    \u003Cdiv style=\"margin: 10px 0\">\n      \u003Cel-button type=\"danger\" plain @click=\"delBatch\">批量删除\u003C\u002Fel-button>\n    \u003C\u002Fdiv>\n    \u003Cel-table :data=\"tableData\" stripe :header-cell-style=\"{ backgroundColor: 'aliceblue', color: '#666' }\"\n              @selection-change=\"handleSelectionChange\">\n      \u003Cel-table-column type=\"selection\" width=\"55\" align=\"center\">\u003C\u002Fel-table-column>\n      \u003Cel-table-column prop=\"id\" label=\"序号\" width=\"70\" align=\"center\">\u003C\u002Fel-table-column>\n      \u003Cel-table-column prop=\"operation\" label=\"操作模块\">\u003C\u002Fel-table-column>\n      \u003Cel-table-column prop=\"type\" label=\"操作类型\">\n        \u003Ctemplate v-slot=\"scope\">\n          \u003Cel-tag type=\"primary\" v-if=\"scope.row.type === '新增'\">{{ scope.row.type }}\u003C\u002Fel-tag>\n          \u003Cel-tag type=\"info\" v-if=\"scope.row.type === '修改'\">{{ scope.row.type }}\u003C\u002Fel-tag>\n          \u003Cel-tag type=\"danger\" v-if=\"scope.row.type === '删除'\">{{ scope.row.type }}\u003C\u002Fel-tag>\n          \u003Cel-tag type=\"danger\" v-if=\"scope.row.type === '批量删除'\">{{ scope.row.type }}\u003C\u002Fel-tag>\n          \u003Cel-tag type=\"success\" v-if=\"scope.row.type === '登录'\">{{ scope.row.type }}\u003C\u002Fel-tag>\n          \u003Cel-tag type=\"success\" v-if=\"scope.row.type === '注册'\">{{ scope.row.type }}\u003C\u002Fel-tag>\n        \u003C\u002Ftemplate>\n      \u003C\u002Fel-table-column>\n      \u003Cel-table-column prop=\"ip\" label=\"操作人IP\">\u003C\u002Fel-table-column>\n      \u003Cel-table-column prop=\"user\" label=\"操作人\">\u003C\u002Fel-table-column>\n      \u003Cel-table-column prop=\"time\" label=\"操作时间\">\u003C\u002Fel-table-column>\n      \u003Cel-table-column label=\"操作\" align=\"center\" width=\"180\">\n        \u003Ctemplate v-slot=\"scope\">\n          \u003Cel-button size=\"mini\" type=\"danger\" plain @click=\"del(scope.row.id)\">删除\u003C\u002Fel-button>\n        \u003C\u002Ftemplate>\n      \u003C\u002Fel-table-column>\n    \u003C\u002Fel-table>\n\n    \u003Cdiv style=\"margin: 10px 0\">\n      \u003Cel-pagination\n          @current-change=\"handleCurrentChange\"\n          :current-page=\"pageNum\"\n          :page-size=\"pageSize\"\n          layout=\"total, prev, pager, next\"\n          :total=\"total\">\n      \u003C\u002Fel-pagination>\n    \u003C\u002Fdiv>\n\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\n\nexport default {\n  name: \"Logs\",\n  data() {\n    return {\n      tableData: [],  \u002F\u002F 所有的数据\n      pageNum: 1,   \u002F\u002F 当前的页码\n      pageSize: 5,  \u002F\u002F 每页显示的个数\n      operation: '',\n      total: 0,\n      form: {},\n      user: JSON.parse(localStorage.getItem('honey-user') || '{}'),\n      ids: [],\n      type: '',\n      optUser: ''\n    }\n  },\n  created() {\n    this.load()\n  },\n  methods: {\n    delBatch() {\n      if (!this.ids.length) {\n        this.$message.warning('请选择数据')\n        return\n      }\n      this.$confirm('您确认批量删除这些数据吗？', '确认删除', {type: \"warning\"}).then(response => {\n        this.$request.delete('\u002Flogs\u002Fdelete\u002Fbatch', {data: this.ids}).then(res => {\n          if (res.code === '200') {   \u002F\u002F 表示操作成功\n            this.$message.success('操作成功')\n            this.load(1)\n          } else {\n            this.$message.error(res.msg)  \u002F\u002F 弹出错误的信息\n          }\n        })\n      }).catch(() => {\n      })\n    },\n    handleSelectionChange(rows) {   \u002F\u002F 当前选中的所有的行数据\n      this.ids = rows.map(v => v.id)\n    },\n    del(id) {\n      this.$confirm('您确认删除吗？', '确认删除', {type: \"warning\"}).then(response => {\n        this.$request.delete('\u002Flogs\u002Fdelete\u002F' + id).then(res => {\n          if (res.code === '200') {   \u002F\u002F 表示操作成功\n            this.$message.success('操作成功')\n            this.load(1)\n          } else {\n            this.$message.error(res.msg)  \u002F\u002F 弹出错误的信息\n          }\n        })\n      }).catch(() => {\n      })\n    },\n    reset() {\n      this.operation = ''\n      this.type = ''\n      this.optUser = ''\n      this.load()\n    },\n    load(pageNum) {  \u002F\u002F 分页查询\n      if (pageNum) this.pageNum = pageNum\n      this.$request.get('\u002Flogs\u002FselectByPage', {\n        params: {\n          pageNum: this.pageNum,\n          pageSize: this.pageSize,\n          operation: this.operation,\n          type: this.type,\n          user: this.optUser,\n        }\n      }).then(res => {\n        this.tableData = res.data.records\n        this.total = res.data.total\n      })\n    },\n    handleCurrentChange(pageNum) {\n      this.load(pageNum)\n    },\n  }\n}\n\u003C\u002Fscript>\n\n\u003Cstyle>\n.el-tooltip__popper {\n  max-width: 300px !important;\n}\n\u003C\u002Fstyle>\n```\n","coding",1,1159,76,"2024-04-16 02:32:11","2026-05-03 22:49:02","【青哥带小白做毕设2024】完整教程资料汇总","qingge-graduation-project-2024",{"project":18,"items":19},{"id":6,"title":15,"slug":16},[20,28,35,42,49,56,63,69,76,83,90,97,104,111,118,125,132,139,146,153,160,167,168],{"id":21,"uuid":22,"project_id":6,"title":23,"type":9,"status":10,"public_enabled":10,"views":24,"sort":25,"created_at":26,"updated_at":27,"project_title":15,"project_slug":16},33,"R1oMCsCX","00. 从0开始带小白做SpringBoot+Vue+uniapp微信小程序实战项目",12130,55,"2025-04-08 11:28:17","2026-05-07 15:33:28.189425+00",{"id":29,"uuid":30,"project_id":6,"title":31,"type":9,"status":10,"public_enabled":10,"views":32,"sort":33,"created_at":34,"updated_at":14,"project_title":15,"project_slug":16},34,"s3u3u8W7","01. 网页布局技巧",3326,56,"2025-04-08 11:28:13",{"id":36,"uuid":37,"project_id":6,"title":38,"type":9,"status":10,"public_enabled":10,"views":39,"sort":40,"created_at":41,"updated_at":14,"project_title":15,"project_slug":16},35,"21zUHQYS","02. JavaScript入门",2017,57,"2025-04-08 11:27:55",{"id":43,"uuid":44,"project_id":6,"title":45,"type":9,"status":10,"public_enabled":10,"views":46,"sort":47,"created_at":48,"updated_at":14,"project_title":15,"project_slug":16},36,"4XVgY9Ti","03. Vue脚手架搭建",3719,58,"2025-04-08 11:27:46",{"id":50,"uuid":51,"project_id":6,"title":52,"type":9,"status":10,"public_enabled":10,"views":53,"sort":54,"created_at":55,"updated_at":14,"project_title":15,"project_slug":16},37,"S8vLLLvk","04. Git速成，推送代码到云端",1585,59,"2025-04-08 11:27:41",{"id":57,"uuid":58,"project_id":6,"title":59,"type":9,"status":10,"public_enabled":10,"views":60,"sort":61,"created_at":62,"updated_at":14,"project_title":15,"project_slug":16},38,"9EbwnGDp","05. 网页布局神器ElementUI速成",2670,60,"2025-04-08 11:27:37",{"id":6,"uuid":64,"project_id":6,"title":65,"type":9,"status":10,"public_enabled":10,"views":66,"sort":67,"created_at":68,"updated_at":14,"project_title":15,"project_slug":16},"tmzahWer","06. Vue管理系统速成",3744,61,"2025-04-08 11:27:32",{"id":70,"uuid":71,"project_id":6,"title":72,"type":9,"status":10,"public_enabled":10,"views":73,"sort":74,"created_at":75,"updated_at":14,"project_title":15,"project_slug":16},40,"2agqAUQK","07. SpringBoot速成",3654,62,"2025-04-08 11:27:27",{"id":77,"uuid":78,"project_id":6,"title":79,"type":9,"status":10,"public_enabled":10,"views":80,"sort":81,"created_at":82,"updated_at":14,"project_title":15,"project_slug":16},41,"SXPAzgy7","08. Http扫盲，让小白也能听懂",2337,63,"2025-04-08 11:27:20",{"id":84,"uuid":85,"project_id":6,"title":86,"type":9,"status":10,"public_enabled":10,"views":87,"sort":88,"created_at":89,"updated_at":14,"project_title":15,"project_slug":16},42,"ostBIxAV","09. SpringBoot集成Mybatis实现增删改查",4190,64,"2025-04-08 11:27:13",{"id":91,"uuid":92,"project_id":6,"title":93,"type":9,"status":10,"public_enabled":10,"views":94,"sort":95,"created_at":96,"updated_at":14,"project_title":15,"project_slug":16},43,"6Sv7afpa","10. Vue封装前后端数据交互工具",3716,65,"2024-04-16 02:33:13",{"id":98,"uuid":99,"project_id":6,"title":100,"type":9,"status":10,"public_enabled":10,"views":101,"sort":102,"created_at":103,"updated_at":14,"project_title":15,"project_slug":16},44,"d53BPIQs","11. Vue登录（含验证码）、注册页面开发",4867,66,"2024-04-16 02:33:08",{"id":105,"uuid":106,"project_id":6,"title":107,"type":9,"status":10,"public_enabled":10,"views":108,"sort":109,"created_at":110,"updated_at":14,"project_title":15,"project_slug":16},45,"m033ng06","12. SpringBoot集成JWT token实现权限验证",3243,67,"2024-04-16 02:33:00",{"id":112,"uuid":113,"project_id":6,"title":114,"type":9,"status":10,"public_enabled":10,"views":115,"sort":116,"created_at":117,"updated_at":14,"project_title":15,"project_slug":16},46,"7xzyVD06","13. SpringBoot+Vue实现单文件、多文件上传和下载",2784,68,"2024-04-16 02:32:52",{"id":119,"uuid":120,"project_id":6,"title":121,"type":9,"status":10,"public_enabled":10,"views":122,"sort":123,"created_at":124,"updated_at":14,"project_title":15,"project_slug":16},47,"BdOLUenp","14. 多角色登录（Vue-Router路由守卫）",2318,69,"2024-04-16 02:32:39",{"id":126,"uuid":127,"project_id":6,"title":128,"type":9,"status":10,"public_enabled":10,"views":129,"sort":130,"created_at":131,"updated_at":14,"project_title":15,"project_slug":16},48,"2Wkx3igg","15. Vue个人信息修改、修改密码、重置密码",2092,70,"2024-04-16 02:32:33",{"id":133,"uuid":134,"project_id":6,"title":135,"type":9,"status":10,"public_enabled":10,"views":136,"sort":137,"created_at":138,"updated_at":14,"project_title":15,"project_slug":16},49,"BDvVa4By","16. SpringBoot+Vue管理系统实现增删改查",2598,71,"2024-04-16 02:32:29",{"id":140,"uuid":141,"project_id":6,"title":142,"type":9,"status":10,"public_enabled":10,"views":143,"sort":144,"created_at":145,"updated_at":14,"project_title":15,"project_slug":16},50,"FJVl0rCu","17. SpringBoot+Vue实现数据的批量导入和导出",1684,72,"2024-04-16 02:32:26",{"id":147,"uuid":148,"project_id":6,"title":149,"type":9,"status":10,"public_enabled":10,"views":150,"sort":151,"created_at":152,"updated_at":14,"project_title":15,"project_slug":16},51,"FvW9oHgj","18. SpringBoot+Vue项目部署上线",2845,73,"2024-04-16 02:32:22",{"id":154,"uuid":155,"project_id":6,"title":156,"type":9,"status":10,"public_enabled":10,"views":157,"sort":158,"created_at":159,"updated_at":14,"project_title":15,"project_slug":16},52,"xyqrxxiR","19. SpringBoot+Vue集成富文本编辑器",1499,74,"2024-04-16 02:32:18",{"id":161,"uuid":162,"project_id":6,"title":163,"type":9,"status":10,"public_enabled":10,"views":164,"sort":165,"created_at":166,"updated_at":14,"project_title":15,"project_slug":16},53,"XAaCXz8W","20. SpringBoot+Vue集成系统公告",1043,75,"2024-04-16 02:32:14",{"id":4,"uuid":5,"project_id":6,"title":7,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},{"id":25,"uuid":169,"project_id":6,"title":170,"type":9,"status":10,"public_enabled":10,"views":171,"sort":172,"created_at":173,"updated_at":14,"project_title":15,"project_slug":16},"ObvLqJdX","22. SpringBoot+Vue实现Echarts数据报表（柱状图、饼图、折线图）",1688,99,"2024-04-16 02:30:25"]