ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SSM+Vue电子资源管理系统毕业设计实战指南

SSM+Vue电子资源管理系统毕业设计实战指南 简介本资源是一套面向高校计算机专业本科生的Java毕业设计完整实践方案聚焦电子资源管理系统的开发与交付适用于课程设计、毕设开题与中期开发参考。系统采用SSMSpringSpringMVCMyBatis后端架构与Vue前端框架实现首页展示、用户管理、电子资源全生命周期管理含列表、日志、收藏、个人中心及系统管理等核心模块覆盖典型Web应用开发全流程。压缩包共446个文件包含112个Java业务逻辑与DAO层代码、45个Vue组件含预览中可见的IndexMain、BreadCrumbs等布局与功能组件、24个JS交互脚本、20个XML配置文件以及SQL建表语句、批处理脚本bat、样式文件与数据库备份等结构完整、开箱即用总大小12.81MB。目前已有175人学习下载提供从环境搭建JDK1.8/Tomcat7/MySQL5.7、源码编译、数据库初始化到前后端联调的完整支撑材料含说明文档、论文LW、答辩PPT显著降低毕设实施门槛。1. 为什么一个「SSM Vue 的电子资源管理系统」毕业设计能帮你稳过答辩、拿下高分、甚至反向收割面试官不是所有 Java 毕业设计都叫「电子资源管理系统」。它不堆砌炫技功能但每一块都踩在高校毕设评审的硬指标上有完整前后端分离架构SSM 后端 Vue 前端、真实业务闭环资源上传→分类管理→权限控制→在线预览/下载、可运行可演示的本地环境、带注释的源码、逻辑清晰的说明文档LW、结构严谨的答辩 PPT——这五件套是导师一眼认出“这学生真干了活”的黄金组合。它不是玩具项目。你能在系统里上传 PDF、PPT、MP4、ZIP 等常见教学资源按学院/课程/年级打标签设置教师可上传、学生仅查看、管理员全权限Vue 前端用 Element UI 实现响应式列表与搜索SSM 后端用 MyBatis 处理多表关联用户-角色-资源-日志Spring MVC 做 REST 接口Spring AOP 统一日志埋点。这些不是“为了用而用”而是高校课程设计中真实存在的数据流与权限边界。适合三类人大四学生没实习、没项目经验急需一个“能跑、能讲、能改、能交”的压舱石项目转行自学者想用一个完整系统串联 Java Web 全栈链路拒绝碎片化教程Java 面试冲刺者把这套系统吃透能当场画出请求流程图、说出 MyBatis 一级缓存失效场景、解释 Vue Router 路由守卫怎么拦截未登录访问——比背十道“冒泡排序Java”实在得多。这不是“抄完就扔”的压缩包而是一份可延展的技术脚手架你加个全文检索集成 Elasticsearch就是课程资料库升级接入 CAS 单点登录就能对接学校统一身份认证把资源预览从 iframe 嵌入 PDF 改成 PDF.js 渲染立刻体现工程细节把控力。下面我们就从零开始把它真正跑起来、调明白、改得动。2. 用 SSM 搭建后端服务从数据库建模到 REST 接口暴露一步不跳过2.1 数据库设计为什么这 5 张表就覆盖了电子资源管理的核心业务毕业设计最怕“表建得太多却用不上”。本系统精简为 5 张核心表全部满足第三范式且字段命名直白、无歧义方便你答辩时快速讲清关系表名主要字段业务含义关键约束sys_userid,username,password,real_name,role_id,status用户基础信息含角色绑定role_id外键 →sys_role.idsys_roleid,role_name,description角色定义如admin / teacher / student—resource_infoid,title,file_path,file_size,file_type,upload_time,uploader_id,category_id,download_count资源元数据含物理路径与统计uploader_id→sys_user.idcategory_id→resource_category.idresource_categoryid,category_name,parent_id,level多级分类如计算机/Java/框架/SSMparent_id自关联支持树形展开sys_logid,user_id,operation,method,params,ip,create_time操作日志用于答辩时展示“系统可观测性”user_id可为空匿名下载提示file_path存的是相对路径如/upload/2024/06/abc.pdf实际文件存在src/main/resources/static/upload/下避免绝对路径导致部署失败。这是很多同学打包后“资源打不开”的第一坑。建库 SQL 直接执行MySQL 5.7-- 创建数据库字符集必须 utf8mb4否则中文文件名乱码 CREATE DATABASE IF NOT EXISTS erms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE erms; -- 用户表 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(30), role_id INT NOT NULL, status TINYINT DEFAULT 1 COMMENT 1:启用,0:禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 分类表支持两级分类parent_id0为一级 CREATE TABLE resource_category ( id INT PRIMARY KEY AUTO_INCREMENT, category_name VARCHAR(50) NOT NULL, parent_id INT DEFAULT 0, level TINYINT DEFAULT 1 COMMENT 1:一级,2:二级, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 资源主表 CREATE TABLE resource_info ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, file_path VARCHAR(300) NOT NULL, file_size BIGINT NOT NULL COMMENT 单位字节, file_type VARCHAR(50) NOT NULL COMMENT 如 application/pdf, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, uploader_id BIGINT NOT NULL, category_id INT NOT NULL, download_count INT DEFAULT 0, description TEXT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;2.2 SSM 三层结构落地Controller 层怎么写才不被导师问“为什么不用 RestController”SSM 不是三个框架简单拼凑而是有明确职责边界的协作链。我们以「资源列表查询」为例拆解每一层的真实写法与设计意图① Mapper 接口DAO 层——只定义不实现// com.erms.mapper.ResourceInfoMapper.java public interface ResourceInfoMapper { // 根据分类ID和关键词分页查资源MyBatis 动态SQL核心 ListResourceInfo selectByCategoryAndKeyword(Param(categoryId) Integer categoryId, Param(keyword) String keyword, Param(offset) Integer offset, Param(limit) Integer limit); // 统计总数配合分页 int countByCategoryAndKeyword(Param(categoryId) Integer categoryId, Param(keyword) String keyword); }说明Param注解必不可少否则 MyBatis 无法识别多个参数报Parameter xxx not found。这是新手高频翻车点。不要省略哪怕只传一个参数也建议加上保持风格统一。② Service 接口与实现业务逻辑层——做判断、加事务、调多个Mapper// com.erms.service.ResourceService.java public interface ResourceService { PageResultResourceInfo listResources(Integer categoryId, String keyword, Integer pageNum, Integer pageSize); } // com.erms.service.impl.ResourceServiceImpl.java Service public class ResourceServiceImpl implements ResourceService { Autowired private ResourceInfoMapper resourceInfoMapper; Override Transactional(readOnly true) // 明确声明只读提升数据库性能 public PageResultResourceInfo listResources(Integer categoryId, String keyword, Integer pageNum, Integer pageSize) { // 1. 参数校验毕业设计必须体现健壮性 if (pageNum null || pageNum 1) pageNum 1; if (pageSize null || pageSize 10) pageSize 10; if (pageSize 100) pageSize 100; // 防止恶意 limit 1000000 // 2. 计算分页偏移量避免前端传错 offset int offset (pageNum - 1) * pageSize; // 3. 查询数据 ListResourceInfo list resourceInfoMapper.selectByCategoryAndKeyword(categoryId, keyword, offset, pageSize); int total resourceInfoMapper.countByCategoryAndKeyword(categoryId, keyword); // 4. 封装分页结果自定义 PageResult 类比 PageHelper 更透明 return new PageResult(list, total, pageNum, pageSize); } }说明PageResult是一个 POJO 类含ListT list,long total,int pageNum,int pageSize,int pages字段。不要直接返回 PageHelper 的 Page 对象——导师会质疑“你懂分页原理吗还是只会套壳” 自己封装答辩时能说清offset (pageNum-1)*pageSize的推导过程。③ Controller 层表现层——接收 HTTP 请求返回 JSON// com.erms.controller.ResourceController.java RestController RequestMapping(/api/resource) public class ResourceController { Autowired private ResourceService resourceService; /** * 获取资源列表GET /api/resource/list?categoryId1keywordjavapageNum1pageSize10 * 注意这里用 RequestParam 显式接收比 RequestBody 更符合 RESTful 语义 */ GetMapping(/list) public ResultPageResultResourceInfo listResources( RequestParam(required false) Integer categoryId, RequestParam(required false) String keyword, RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize) { PageResultResourceInfo pageResult resourceService.listResources(categoryId, keyword, pageNum, pageSize); return Result.success(pageResult); // Result 是统一封装的响应体含 code/msg/data } }说明RestController是Controller ResponseBody的组合注解必须用它而不是Controller。否则返回对象不会自动转 JSON前端收不到数据——这是本地调试时“接口返回空白”的头号原因。Result.success()返回格式为{code:200,msg:success,data:{...}}Vue 前端可直接解构response.data。3. Vue 前端工程搭建与核心页面实现从环境配置到在线预览 PDF3.1 Vue 2.6 Element UI 环境初始化为什么不用 Vue 3因为毕业设计要“稳”当前主流是 Vue 3但本系统采用 Vue 2.6.14 Element UI 2.15.14 组合理由很务实兼容性好Element UI 官方对 Vue 2 的文档、示例、社区答疑最全遇到问题搜“element ui vue2 upload”立刻出答案学习成本低Vue 2 的 Options APIdata/methods/computed比 Vue 3 的 Composition API 更易理解答辩时画流程图更直观生态成熟vue-router 3.x、vuex 3.x、axios全部稳定不会出现“npm install 后一堆 peer dependency warning”。初始化命令Node.js ≥ 14# 1. 全局安装 vue-cli如果未安装 npm install -g vue/cli4.5.15 # 2. 创建 Vue 2 项目关键指定 --preset vue2 vue create erms-frontend --preset vue2 # 3. 进入项目安装 Element UI 和 axios cd erms-frontend npm install element-ui2.15.14 axios0.21.4 # 4. 在 main.js 中全局引入 Element UI // src/main.js import Vue from vue import App from ./App.vue import router from ./router import store from ./store import ElementUI from element-ui import element-ui/lib/theme-chalk/index.css Vue.use(ElementUI) // 必须放在 new Vue() 之前 new Vue({ router, store, render: h h(App) }).$mount(#app)3.2 资源列表页ResourceList.vue如何用 Element Table 实现带搜索、分页、状态标签的表格这是系统最核心的页面也是答辩演示必播片段。代码需体现“真实业务感”而非纯技术 Demo!-- src/views/resource/ResourceList.vue -- template div classresource-list !-- 搜索区域 -- el-card classbox-card shadownever div slotheader classclearfix span资源检索/span /div el-form :inlinetrue :modelsearchForm sizesmall el-form-item label分类 el-select v-modelsearchForm.categoryId placeholder全部分类 clearable el-option v-foritem in categoryOptions :keyitem.id :labelitem.categoryName :valueitem.id /el-option /el-select /el-form-item el-form-item label关键词 el-input v-modelsearchForm.keyword placeholder输入标题或描述 clearable/el-input /el-form-item el-form-item el-button typeprimary clickhandleSearch搜索/el-button el-button clickresetSearch重置/el-button /el-form-item /el-form /el-card !-- 数据表格 -- el-card classbox-card stylemargin-top: 15px; div slotheader classclearfix span资源列表/span el-button stylefloat: right; padding: 3px 0 typetext clickrefreshList刷新/el-button /div el-table :datatableData stripe border stylewidth: 100% el-table-column proptitle label资源标题 width300/el-table-column el-table-column propcategoryName label所属分类 width180/el-table-column el-table-column propuploaderName label上传人 width120/el-table-column el-table-column propfileSize label大小 width120 template slot-scope{row} {{ formatFileSize(row.fileSize) }} /template /el-table-column el-table-column propuploadTime label上传时间 width160/el-table-column el-table-column propdownloadCount label下载次数 width100/el-table-column el-table-column label操作 width180 template slot-scope{row} !-- PDF/DOCX/PPTX 文件支持在线预览 -- el-button v-if[application/pdf, application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation].includes(row.fileType) sizemini typeprimary clickhandlePreview(row) 预览 /el-button !-- 其他文件提供下载 -- el-button v-else sizemini typesuccess clickhandleDownload(row) 下载 /el-button !-- 管理员/教师可编辑 -- el-button v-ifisEditable sizemini typeinfo clickhandleEdit(row) 编辑 /el-button /template /el-table-column /el-table !-- 分页器 -- div classpagination-container el-pagination size-changehandleSizeChange current-changehandleCurrentChange :current-pagecurrentPage :page-sizes[10, 20, 50, 100] :page-sizepageSize layouttotal, sizes, prev, pager, next, jumper :totaltotal /el-pagination /div /el-card /div /template script import { getResourceList } from /api/resource import { getCategoryList } from /api/category import { formatFileSize } from /utils/file export default { name: ResourceList, data() { return { searchForm: { categoryId: null, keyword: }, tableData: [], categoryOptions: [], // 分类下拉选项 currentPage: 1, pageSize: 10, total: 0, isEditable: false // 根据登录用户角色动态控制 } }, created() { this.loadCategories() this.loadResources() this.checkPermission() }, methods: { // 加载分类列表 async loadCategories() { try { const res await getCategoryList() this.categoryOptions res.data } catch (err) { this.$message.error(加载分类失败 err.message) } }, // 加载资源列表 async loadResources() { try { const params { categoryId: this.searchForm.categoryId, keyword: this.searchForm.keyword, pageNum: this.currentPage, pageSize: this.pageSize } const res await getResourceList(params) this.tableData res.data.list this.total res.data.total } catch (err) { this.$message.error(加载资源失败 err.message) } }, // 检查当前用户是否具备编辑权限简化版登录后从 localStorage 读角色 checkPermission() { const userInfo JSON.parse(localStorage.getItem(userInfo) || {}) this.isEditable [admin, teacher].includes(userInfo.role) }, // 搜索 handleSearch() { this.currentPage 1 this.loadResources() }, resetSearch() { this.searchForm { categoryId: null, keyword: } this.handleSearch() }, // 分页相关 handleSizeChange(val) { this.pageSize val this.currentPage 1 this.loadResources() }, handleCurrentChange(val) { this.currentPage val this.loadResources() }, // 预览PDF等文件关键用 iframe 嵌入后端静态资源路径 handlePreview(row) { const url /static${row.filePath} // 注意后端 Spring Boot 默认 static 目录映射为 /static window.open(url, _blank) }, // 下载文件 handleDownload(row) { const link document.createElement(a) link.href /static${row.filePath} link.download row.title this.getFileExt(row.fileType) document.body.appendChild(link) link.click() document.body.removeChild(link) }, getFileExt(fileType) { const extMap { application/pdf: .pdf, application/msword: .doc, application/vnd.openxmlformats-officedocument.wordprocessingml.document: .docx, application/vnd.ms-powerpoint: .ppt, application/vnd.openxmlformats-officedocument.presentationml.presentation: .pptx, application/zip: .zip, application/x-rar-compressed: .rar } return extMap[fileType] || .bin }, refreshList() { this.loadResources() } }, filters: { formatFileSize } } /script说明handlePreview方法直接window.open(/static/xxx.pdf)依赖 Spring Boot 的静态资源默认配置spring.resources.static-locationsclasspath:/static/。不要用 axios 下载再 blob URL 预览——增加复杂度且 PDF.js 需额外集成毕业设计阶段iframe 嵌入最稳妥。getFileExt是根据file_typeMIME Type映射文件扩展名解决后端未保存原始文件名的问题。这是真实业务中常见的“元数据不全”场景体现你考虑了数据完整性。所有 API 调用都封装在/api/目录下如getResourceList内部使用axios并统一处理 baseURL 和 token若需登录态。4. 前后端联调与部署从本地启动到 jar 包运行绕开 90% 的环境坑4.1 启动顺序与端口规划为什么必须先启后端再启前端这是一个极易被忽视的“启动时序陷阱”。很多同学npm run serve启动 Vue发现接口 404第一反应是“后端没写好”其实是跨域和路径没配对。标准启动流程务必严格遵守启动后端Spring Bootcd erms-backend mvn spring-boot:run # 默认端口 8080访问 http://localhost:8080/api/resource/list 应返回 JSON配置前端代理开发时解决跨域在erms-frontend/vue.config.js中添加module.exports { devServer: { port: 8081, // 前端开发端口避开后端 8080 proxy: { /api: { target: http://localhost:8080, // 代理到后端 changeOrigin: true, pathRewrite: { ^/api: /api // 保持前缀不变 } } } } }说明这样axios.get(/api/resource/list)实际请求http://localhost:8080/api/resource/list浏览器地址栏仍是http://localhost:8081无跨域报错。切勿在 axios baseURL 写死http://localhost:8080——上线后要改且本地开发时 Chrome 会报 CORS。启动前端Vuecd erms-frontend npm run serve # 访问 http://localhost:8081此时所有接口走代理数据正常渲染4.2 打包部署如何生成可运行的 jar 包并让 Vue 静态资源被 Spring Boot 正确服务毕业设计交付物要求“双可运行”既能在 IDEA 里调试也能打包成独立 jar 运行。关键在两点后端打包mvn clean package -Dmaven.test.skiptrue生成target/erms-backend-1.0.jar前端构建npm run build生成dist/目录将dist/下所有文件含index.html,js/,css/复制到erms-backend/src/main/resources/static/下。为什么放static/因为 Spring Boot 默认将classpath:/static/映射为根路径/。这样http://localhost:8080/就能访问 Vue 的index.htmlhttp://localhost:8080/js/app.xxx.js能正确加载。严禁把dist/整个目录扔进static/必须把dist/内容index.html,js/,css/,img/平铺到static/下否则路径错乱。验证 jar 包是否成功# 运行 jar确保 8080 端口空闲 java -jar target/erms-backend-1.0.jar # 浏览器访问 # http://localhost:8080/ → Vue 前端首页 # http://localhost:8080/api/resource/list → 后端接口 # http://localhost:8080/static/upload/test.pdf → 上传的 PDF 文件若已上传5. 避坑指南那些让答辩前夜崩溃的 5 个真实问题及血泪解决方案5.1 现象前端页面空白控制台报Failed to load resource: the server responded with a status of 404 ()原因最常见vue.config.js代理配置错误比如target写成http://localhost:8080/末尾斜杠导致路径拼接异常或axios请求路径写成/api//resource/list双斜杠或后端 Controller 的RequestMapping(/api/resource)和前端axios.get(/api/resource/list)前缀不一致。解决打开浏览器开发者工具 → Network 标签页 → 点击一个失败请求 → 看Headers中的Request URL是什么如果是http://localhost:8081/api/resource/list说明代理没生效检查vue.config.js如果是http://localhost:8080/api/resource/list但返回 404说明后端没启动或路径写错用curl http://localhost:8080/api/resource/list直接测试后端。5.2 现象上传文件后数据库file_path字段存的是/upload/2024/06/xxx.pdf但访问http://localhost:8080/static/upload/2024/06/xxx.pdf报 404原因Spring Boot 默认静态资源路径是classpath:/static/、classpath:/public/、classpath:/resources/、classpath:/META-INF/resources/不包含upload/目录你把文件物理存到了src/main/resources/static/upload/但 Spring Boot 启动时只扫描static/下的启动时已存在的文件运行时创建的子目录如upload/2024/06/不会被自动映射。解决方案一推荐在application.yml中显式配置上传目录为静态资源路径spring: resources: static-locations: classpath:/static/,file:./upload/然后把文件上传到项目根目录下的upload/文件夹./upload/2024/06/xxx.pdf这样http://localhost:8080/upload/2024/06/xxx.pdf就能访问。方案二用Controller写一个文件下载接口通过ResponseEntityResource返回FileSystemResource绕过静态资源限制。但毕业设计没必要增加复杂度。5.3 现象登录后前端localStorage存了userInfo但刷新页面就丢失登录态菜单栏变回游客视图原因Vue Router 的路由守卫router.beforeEach里只检查了localStorage.getItem(token)但没检查userInfo是否有效或userInfo存的是字符串取出来没JSON.parse就直接用导致userInfo.role是undefined。解决在router/index.js的全局前置守卫中必须解析并校验router.beforeEach((to, from, next) { const token localStorage.getItem(token) const userInfoStr localStorage.getItem(userInfo) const userInfo userInfoStr ? JSON.parse(userInfoStr) : null if (to.meta.requiresAuth (!token || !userInfo)) { next(/login) } else { next() } })登录成功后localStorage.setItem(userInfo, JSON.stringify(res.data.userInfo))确保存的是字符串。5.4 现象MyBatis 查询返回null但数据库明明有数据或#{}取值为空${}才有值原因#{}是预编译占位符安全${}是字符串拼接危险当你用Param(categoryId)传参但 XML 中写if testcategoryId ! null and categoryId ! 而categoryId是Integer类型! 永远为false类型不匹配导致条件未生效SQL 变成WHERE 11查出所有数据或空数据。解决XML 中判空只用! nullif testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null and keyword ! AND (title LIKE CONCAT(%, #{keyword}, %) OR description LIKE CONCAT(%, #{keyword}, %)) /if永远不要对Integer用! 对String用! null即可MyBatis 3.4 会自动处理空字符串。5.5 现象答辩现场演示时点击“预览 PDF”弹出新窗口但显示 “Failed to load PDF document”原因PDF 文件本身损坏上传时网络中断或 MIME Type 错误后端存的file_type是text/plain但实际是 PDF浏览器拒绝渲染或路径含中文/static/upload/实验报告.pdf某些浏览器对 URL 中中文编码处理不一致。解决上传时用MultipartFile.getContentType()获取真实 MIME Type不要依赖文件扩展名文件名存储用 UUID 重命名如a1b2c3d4.pdffile_path存重命名后的路径title字段存原始文件名预览时用window.open(/static/ row.filePath)确保路径纯英文、无空格、无特殊符号。6. 答辩加分技巧3 个让导师眼前一亮的“小而深”改造点6.1 给资源列表加“热度排序”用下载次数 时间衰减让优质资源浮出水面毕业设计常被诟病“功能平庸”。一个简单的排序优化就能体现你对业务的理解深度。不改后端 SQL只在 Controller 层加一行// ResourceController.java GetMapping(/list) public ResultPageResultResourceInfo listResources(...) { PageResultResourceInfo pageResult resourceService.listResources(...); // ✅ 新增按热度排序下载数 × 权重权重 1 / (天数 1) ListResourceInfo sortedList pageResult.getList().stream() .sorted((a, b) - { long now System.currentTimeMillis(); long daysA (now - a.getUploadTime().getTime()) / (1000 * 60 * 60 * 24) 1; long daysB (now - b.getUploadTime().getTime()) / (1000 * 60 * 60 * 24) 1; double heatA a.getDownloadCount() * 1.0 / daysA; double heatB b.getDownloadCount() * 1.0 / daysB; return Double.compare(heatB, heatA); // 降序 }) .collect(Collectors.toList()); pageResult.setList(sortedList); return Result.success(pageResult); }说明这个公式叫“时间衰减热度”意思是一个 100 次下载的 1 天前资源热度 100/2 50一个 200 次下载的 10 天前资源热度 200/11 ≈ 18.18。它让新上传的优质资源有机会曝光比单纯按download_count DESC更合理。答辩时说一句“我参考了知乎热榜的排序逻辑”导师会点头。6.2 Vue 前端加“防抖搜索”避免用户每敲一个字就发请求当前handleSearch是手动点击触发但你可以升级为输入框实时搜索加防抖。修改ResourceList.vueel-input v-modelsearchForm.keyword placeholder输入标题或描述 clearable inputdebouncedSearch !-- 改为 input 触发 -- /el-input// 在 methods 下新增 methods: { // 防抖函数使用 Lodash或自己写 debouncedSearch: _.debounce(function() { this.currentPage 1 this.loadResources() }, 500), // 500ms 内连续输入只执行最后一次 }说明_.debounce需要npm install lodash。效果是用户输入“java ssm”不会发 3 次请求j / ja / jav / java ...而是在停顿 500ms 后发一次keywordjava ssm。这体现你关注用户体验和服务器负载不是“能跑就行”。6.3 用 Logback 实现操作日志入库 查看把“系统有日志”变成“日志能查、能筛、能导出”很多系统只打印System.out.println答辩时被问“日志在哪看”就卡壳。我们用 Logback AOP把关键操作上传、下载、删除自动记入sys_log表① 定义自定义注解// com.erms.annotation.LogOperation.java Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface LogOperation { String value() default ; // 操作描述如 上传资源 }② 编写 AOP 切面// com.erms.aspect.LogAspect.java Aspect Component public class LogAspect { Autowired private SysLogMapper sysLogMapper; Around(annotation(logOperation)) public Object recordLog(ProceedingJoinPoint joinPoint, LogOperation p a hrefhttps://download.csdn.net/download/sinat_26552841/87739999 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
返回列表