ARTICLE DETAIL

资讯详情

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

基于SpringBoot与Vue的软件缺陷管理系统设计与实现

基于SpringBoot与Vue的软件缺陷管理系统设计与实现 简介这是一套面向计算机专业本科生的毕业设计级软件缺陷管理系统采用SpringBootVue前后端分离架构解决中小型开发团队或教学场景中缺陷提交、状态跟踪、优先级调度与数据可视化等核心管理需求。资源包共706个文件含136个JavaScript前端逻辑文件、53个Java后端业务代码、33个Vue组件、276张界面截图PNG/JPG及3个MySQL建库建表SQL脚本CSS样式文件覆盖8种主题配色整体压缩包仅3.52MB轻量易部署。目前已有32人学习下载适合Java与前端初学者开展全栈实践尤其利于理解RESTful接口设计、Vue组件通信、数据库第三范式建模及缺陷生命周期管理流程。所有Java类与Vue组件均含规范注释SQL脚本适配MySQL 8.0配套环境要求明确JDK 11、Node.js 14可直接导入IDE运行调试。1. 项目概述与核心价值1.1 这个系统到底解决什么问题软件缺陷管理系统说白了就是Bug管理系统。很多人觉得它不就是“记录bug、改bug”的工具吗但真正开发过的人都知道一个缺陷从被发现到最终关闭中间要经过提交、指派、修复、验证、关闭好几个环节每个环节都涉及不同角色的人如果没有一套系统做流转记录光靠微信群和Excel表格项目到了后期Bug数量一多谁改的、改没改完、改完有没有验证完全是一笔糊涂账。我之前带过一个6人小组的中型项目团队用共享Excel记录缺陷结果上线前统计缺陷状态时发现同一批Bug被重复记录了三次还有几个已经修好的Bug因为没人更新状态测试又提了一遍。这就是手工管理缺陷的典型痛点。所以这个基于SpringBoot与Vue的软件缺陷管理系统本质上是把“缺陷的发现→指派→修复→验证→关闭”这条流程线上所有动作记录下来并让不同角色各司其职系统自动保留操作痕迹最终还能根据这些数据统计出质量报表。1.2 为什么这套技术栈值得参考SpringBoot加Vue这套组合在目前国内中小型项目里的占有率实在太高了。SpringBoot负责后端接口Vue负责页面交互MySQL存数据Redis做缓存可选这个毕设里不加也行前后端通过JSON交互。对于毕业设计来说这套技术栈最大的优势是“生态成熟、资料多、面试也常问”。你需要演示什么功能都有人实现过源码参考丰富不至于卡死在某个冷门技术上。而且SpringBoot自带的Tomcat、自动配置机制可以让开发者把精力集中在业务逻辑而不是环境配置上Vue的组件化开发则让页面复用和维护都省心很多。这个系统适合谁来参考计算机相关专业做毕业设计的同学是最主要的群体。其次想学习前后端分离开发完整流程的初中级开发者也可以照着搭一遍。如果你是在校生花一周时间把核心模块理清楚答辩的时候能把表结构设计、权限设计、状态流转这几个亮点讲明白就足够拿到一个不错的分数了。2. 整体功能设计思路与模块划分2.1 用户角色与权限模型缺陷管理系统最核心的不是写代码而是权限模型的设计。bug谁能提交、谁能分配、谁能关闭必须规定清楚否则系统上线后会乱套。按照最常见的团队配置我设计了四类角色角色核心权限说明管理员用户管理、项目管理、系统配置拥有所有权限可查看全部数据测试人员提交缺陷、验证修复、评论核心使用者负责发现和验证Bug开发人员查看被指派的缺陷、提交修复、评论只能修改分配给自己的缺陷项目负责人项目统计、指派缺陷、关闭缺陷一般由项目经理担任掌握全局这个权限模型在实现上不复杂用一张user表存角色编号后端接口用拦截器鉴权前端再根据角色控制按钮显示就行。注意一点角色权限不要在前端做唯一校验前端只是隐藏按钮真正的权限判断必须在后端接口里做。我见过有的毕设只做了前端按钮隐藏接口直接裸奔任何人调接口都能删数据这在答辩时也是硬伤。2.2 六大核心功能模块系统整体分为六大模块登录认证模块、项目管理模块、缺陷管理模块、统计分析模块、用户管理模块、操作日志模块。这里重点讲一下各模块的职责和关联关系。登录认证模块负责用户登录、JWT令牌签发和注销操作。项目管理模块比较简单就是项目的增删改查项目存在的意义是把Bug归类一个项目下挂多个缺陷项目负责人可以对项目成员进行维护。缺陷管理模块是核心中的核心包含缺陷的提交、指派、修复、验证、关闭五种状态操作外加查询、筛选、评论、附件上传等功能。统计分析模块是根据缺陷表的数据做聚合统计比如按严重级别分组统计、按处理人统计、按状态统计并给出图表展示。用户管理模块负责账号的CRUD和角色分配。操作日志模块则记录了关键动作登录、提交Bug、修改状态等的操作人、操作时间、操作内容方便溯源。2.3 缺陷状态流转流程设计缺陷状态是整个系统最关键的领域模型。我参考了业界比较通用的Bug生命周期模型结合毕业设计的演示需求做了简化设计了五个状态新建New→ 已指派Assigned→ 已修复Fixed→ 已验证Verified→ 已关闭Closed状态流转的操作方要卡死测试人员提交缺陷后状态为“新建”系统自动记录提交人和提交时间。项目负责人或管理员看到新建的缺陷指派给某个开发人员状态变为“已指派”同时记录指派人和指派时间。开发人员修改代码后将缺陷状态改为“已修复”写明修复说明此时系统要记录修复人和修复时间。测试人员对修复结果进行回归测试。如果验证通过状态变为“已验证”。如果验证不通过可以直接退回“重建”Reopen重新走“已指派”流程。项目负责人确认缺陷确实已经解决执行“关闭”操作缺陷生命周期结束。我做过调查很多毕设系统里这块容易做成“状态字段随便改”没有任何流转校验。答辩老师可能会提出一个经典问题如果测试人员直接把新建的Bug改成已验证怎么办你的系统如果允许这个操作就会显得设计不严谨。所以我在后端一定要加状态机校验只允许合法状态的迁移非法操作直接抛异常。3. 数据库设计与表结构实现3.1 数据库建模思路数据库是这个系统的地基一开始建模没想清楚后面写代码会一路踩坑。我按照“用户表—项目表—缺陷表—评论表—附件表—日志表”这6张核心表来设计表关系如下图用文字描述用户表与缺陷表存在提交人、指派处理人、修复人三种关联关系项目表与缺陷表是一对多关系缺陷表与评论表是一对多关系缺陷表与附件表是一对多关系。这里有一点特别重要缺陷表里不要直接用外键约束去关联用户表而是用逻辑外键只存userId来关联。理由有两点第一删数据时不会因为外键约束导致各种报错第二MyBatis-Plus联合查询时用逻辑外键更灵活。这在答辩时也可以当作一个设计亮点来提。3.2 核心表结构脚本下面直接给出核心的SQL建表脚本这几张表基本就能覆盖系统所有功能需求-- 用户表 CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录账号, password VARCHAR(100) NOT NULL COMMENT 密码(MD5加密存储), nickname VARCHAR(50) DEFAULT NULL COMMENT 姓名, role TINYINT NOT NULL DEFAULT 3 COMMENT 角色:1管理员 2项目负责人 3开发人员 4测试人员, email VARCHAR(100) DEFAULT NULL, phone VARCHAR(20) DEFAULT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态:1启用 0禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 用户表; -- 项目表 CREATE TABLE project ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL COMMENT 项目名称, description VARCHAR(500) COMMENT 项目描述, manager_id INT COMMENT 负责人ID, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT 项目表; -- 缺陷表 CREATE TABLE bug ( id INT PRIMARY KEY AUTO_INCREMENT, project_id INT NOT NULL COMMENT 所属项目ID, title VARCHAR(200) NOT NULL COMMENT 缺陷标题, description TEXT COMMENT 缺陷详细描述, severity TINYINT NOT NULL COMMENT 严重级别:1提示 2一般 3严重 4致命, priority TINYINT NOT NULL DEFAULT 2 COMMENT 优先级:1低 2中 3高, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态:1新建 2已指派 3已修复 4已验证 5已关闭 6重新打开, reporter_id INT NOT NULL COMMENT 提交人ID, assignee_id INT DEFAULT NULL COMMENT 指派人ID, fixer_id INT DEFAULT NULL COMMENT 修复人ID, verifier_id INT DEFAULT NULL COMMENT 验证人ID, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) COMMENT 缺陷表; -- 缺陷评论表 CREATE TABLE bug_comment ( id INT PRIMARY KEY AUTO_INCREMENT, bug_id INT NOT NULL COMMENT 缺陷ID, user_id INT NOT NULL COMMENT 评论人ID, content VARCHAR(1000) NOT NULL COMMENT 评论内容, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 缺陷评论表; -- 附件表 CREATE TABLE attachment ( id INT PRIMARY KEY AUTO_INCREMENT, bug_id INT NOT NULL, file_name VARCHAR(255) NOT NULL, file_url VARCHAR(500) NOT NULL, uploader_id INT NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 附件表; -- 操作日志表 CREATE TABLE operation_log ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, action VARCHAR(200) NOT NULL COMMENT 操作描述, ip VARCHAR(50) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 操作日志表;3.3 关于字段设计的几个关键细节严重级别和状态字段我用的都是TINYINT数字类型而不是直接用字符串。这样可以减少存储空间并且在后端代码里用枚举或常量进行映射前端显示时再通过翻译函数转成文字。这里有一个容易踩的坑如果你的状态直接存字符串“新建”“已指派”后期如果要调整状态名称或者加状态就得写一大堆SQL更新语句而且代码里到处是魔法字符串很容易写错。用数字常量统一管理改起来只需要动枚举或者配置文件一处地方。create_time字段我用的是DATETIME默认值CURRENT_TIMESTAMP这个细节可以省掉很多后端手动set时间的代码。MyBatis-Plus插入数据时如果实体类的该字段为null数据库会自动填充当前时间省心。另外update_time加上了ON UPDATE CURRENT_TIMESTAMP当记录被更新时自动刷新这样就能省去“每次改数据都要手动维护更新时间”的麻烦。4. 后端SpringBoot核心实现细节4.1 项目分层结构与初始化配置后端项目采用经典的四层结构Controller层、Service层、Mapper层、Entity实体层。Controller只负责接收参数和返回结果Service层写业务逻辑Mapper层通过MyBatis-Plus操作数据库。此外还有config包配置类、common包统一返回结果、utils包工具类、interceptor包拦截器。项目创建建议使用Spring InitializrJava版本用JDK 8或11面试和毕设环境最稳SpringBoot版本不要一味求新2.5.x系列就够了。第一次做毕设的同学很容易踩“版本太高导致各种依赖不兼容”的坑比如SpringBoot 3.0以上要求JDK 17而且javax包改为jakarta包网上很多老代码直接不能用折腾半天还在配置阶段打转。pom.xml里核心依赖如下dependencies !-- SpringBoot Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.2/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- JWT -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency !-- Lombok -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- Hutool工具类 -- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.18/version /dependency /dependenciesMyBatis-Plus强烈推荐它让我少写至少60%的CRUD代码。单表查询直接用baseMapper.selectList、selectPage这些现成的方法复杂查询再手写XML比纯MyBatis高效太多了。我在毕设里用MyBatis-Plus的分页插件做分页查询远比自己在SQL里拼Limit方便。4.2 统一返回结果与异常处理前后端分离的项目接口返回值一定要统一格式。我定义了一个R类作为统一返回结果Data public class RT { private Integer code; // 200成功, 401未登录, 403无权限, 500错误 private String message; private T data; public static T RT ok() { return new RT(200, 操作成功, null); } public static T RT ok(T data) { return new RT(200, 操作成功, data); } public static T RT error(String message) { return new RT(500, message, null); } public static T RT error(Integer code, String message) { return new RT(code, message, null); } }这个类看起来很普通但在实际开发中作用很大。接口返回的数据结构一致前端Axios响应拦截器就能统一处理错误提示不用每个请求都写一遍错误分支。有个细节code我用了HTTP风格的数字而不是“true/false”这种布尔值这样前端可以根据code值判断是未登录跳登录页、还是没有权限弹提示框语义更清晰。全局异常处理也要配上用RestControllerAdvice注解统一捕获业务异常和未知异常返回R.error格式避免一报错就把整个Tomcat的异常堆栈直接抛给前端浏览器。4.3 JWT登录鉴权实现登录鉴权我用了JWT方案。用户登录成功之后后端生成一个令牌字符串返回给前端前端存在localStorage里每次请求在请求头里带上Authorization: Bearer token后端拦截器再解析验证。JWT工具类的核心代码public class JwtUtils { private static final String SECRET abcdefghijklmnopqrstuvwxyz0123456789; // 生成令牌 public static String generateToken(Integer userId, String username) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 1000 * 60 * 60 * 24)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } // 解析令牌 public static Claims parseToken(String token) { return Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } }拦截器在这里只做令牌有效性校验用HandlerInterceptor前置拦截所有非登录接口。由于我后面还要做角色权限控制所以拦截器里会顺便解析出userId和role放到ThreadLocal或直接放入request的attribute中Controller层就能直接拿到当前用户信息。关于密码存储这里千万不要明文存密码我用的是MD5加盐实际生产建议BCrypt在用户表里存的是加盐后MD5的密文。答辩时老师如果问密码安全怎么做你答出“MD5加盐存储”和“BCrypt”这两个词至少能证明你不是照着网上源码无脑抄的。4.4 缺陷状态流转的业务校验这里展示一下核心的状态流转逻辑。我在Bug实体中定义了一个状态字段然后控制器里写一个通用的状态更新方法PostMapping(/bug/updateStatus) public R? updateStatus(RequestBody UpdateStatusDTO dto) { // 1. 校验缺陷是否存在 Bug bug bugMapper.selectById(dto.getBugId()); if (bug null) { return R.error(缺陷不存在); } // 2. 状态流转合法性校验核心 Integer currentStatus bug.getStatus(); Integer targetStatus dto.getTargetStatus(); boolean isValid checkStatusTransition(currentStatus, targetStatus, currentUser.getRole()); if (!isValid) { return R.error(非法的状态流转); } // 3. 更新状态字段同时记录操作人信息 bug.setStatus(targetStatus); if (targetStatus 2) { bug.setAssigneeId(dto.getUserId()); } else if (targetStatus 3) { bug.setFixerId(currentUser.getId()); } else if (targetStatus 4) { bug.setVerifierId(currentUser.getId()); } bugMapper.updateById(bug); // 4. 记录操作日志 operationLogService.log(currentUser.getId(), 更新缺陷状态: bug.getId()); return R.ok(); } private boolean checkStatusTransition(Integer oldStatus, Integer newStatus, Integer role) { // 只有合法迁移才放行 switch (oldStatus) { case 1: return newStatus 2 role 2; // 新建-指派, 负责人操作 case 2: return newStatus 3 role 3; // 指派-修复, 开发人员操作 case 3: return newStatus 4 || newStatus 6; // 修复-验证 或 修复-重新打开 case 4: return newStatus 5; // 验证-关闭 case 6: return newStatus 2; // 重新打开-重新指派 default: return false; } }这里有个设计心得如果你把状态流转校验逻辑写成一长串if-else或者switch后期状态一旦变多会非常难看。我后来在另一个项目里直接用了枚举加状态机映射表代码更整洁。如果你时间充裕也可以用Map提前初始化好“当前状态 - 可到达状态集合”的映射扩展起来更方便。4.5 文件上传与静态资源映射缺陷截图是Bug信息中很重要的部分没有截图开发人员有时候根本猜不出问题出在哪。所以系统要支持图片上传前端用上传组件把图片传给后端后端保存到本地磁盘再把访问URL返回给前端。SpringBoot配置文件里设置上传路径spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB # 自定义上传路径 upload: path: D:/upload/上传接口的核心代码PostMapping(/upload) public RString upload(RequestParam(file) MultipartFile file) { if (file.isEmpty()) { return R.error(文件不能为空); } // 生成唯一文件名防止重名覆盖 String originalFilename file.getOriginalFilename(); String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); String newFileName UUID.randomUUID().toString().replace(-, ) suffix; // 创建目录 File dir new File(uploadPath); if (!dir.exists()) { dir.mkdirs(); } // 保存文件 File dest new File(uploadPath newFileName); file.transferTo(dest); // 返回访问路径 return R.ok(/files/ newFileName); }然后写一个配置类实现WebMvcConfigurer接口把/files/**路径映射到本地目录Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/files/**) .addResourceHandler(file: uploadPath); }5. 前端Vue核心实现细节5.1 Vue项目结构与开发环境准备前端使用Vue2加Element UI这套组合Vue3加Element Plus也行但对毕设而言Vue2的参考资料更丰富我建议新手用Vue2。用Vue CLI脚手架创建项目之后我习惯把src目录按下面的方式组织src/ ├── api/ // 接口请求封装 │ ├── bug.js │ ├── user.js │ └── project.js ├── assets/ // 静态资源 ├── components/ // 公共组件 ├── layout/ // 后台布局侧边栏头部 ├── router/ // 路由配置 ├── store/ // Vuex状态管理 ├── utils/ // 工具函数axios封装 └── views/ // 页面组件 ├── login.vue ├── dashboard.vue ├── bug/ │ ├── bugList.vue │ ├── bugDetail.vue │ └── bugForm.vue ├── project/ └── user/关于安装Vue环境常见的坑是node版本与脚手架版本不匹配。建议使用Node 16.20.2版本搭配Vue CLI 5.0以上安装命令就是npm install -g vue/cli安装完用vue -V验证一下。npm安装卡住的时候推荐切换国内镜像源这个网上教程很多这里不展开了。5.2 Axios请求封装与拦截器前端和后台交互最重要的一层就是Axios封装。如果不封装每个页面里都得写token获取、错误弹窗、加载动画这些重复逻辑非常痛苦。我的统一封装是这样的import axios from axios import { Message } from element-ui import router from /router // 创建axios实例 const service axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动携带token service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }, error { return Promise.reject(error) }) // 响应拦截器统一处理code service.interceptors.response.use(response { const res response.data if (res.code 200) { return res } else if (res.code 401) { // 未登录或token过期 localStorage.removeItem(token) router.push(/login) return Promise.reject(new Error(登录已过期)) } else { Message.error(res.message || 系统错误) return Promise.reject(new Error(res.message)) } }, error { Message.error(网络异常) return Promise.reject(error) }) export default service有了这层封装接口文件里只需要写请求路径和参数就行。比如bug.jsimport request from /utils/request // 分页查询缺陷列表 export function getBugList(params) { return request({ url: /bug/list, method: get, params }) } // 提交新缺陷 export function createBug(data) { return request({ url: /bug/create, method: post, data }) } // 更新缺陷状态 export function updateBugStatus(data) { return request({ url: /bug/updateStatus, method: post, data }) }这样每个页面的请求代码都只有几行代码量大大减少而且排查问题时能快速定位。5.3 缺陷列表页与表单弹窗缺陷列表页是整个系统前端最核心的页面。我用Element UI的el-table展示缺陷列表配合el-pagination做分页el-select做状态筛选el-date-picker做时间范围选择。表格要显示缺陷标题、所属项目、严重级别、当前状态、提交人、指派人和提交时间。严重级别用el-tag不同级别配不同颜色致命红色、严重橙色、一般蓝色、提示灰色一眼就能看出最紧急的缺陷是哪些。搜索条件的写法也要注意用户选了状态、项目、时间范围点击“查询”按钮后需要把筛选条件拼成请求参数传给后端。这里有个小坑日期范围选择组件返回的是一个数组需要拆分成beginTime和endTime两个字段再传给后端不然MyBatis-Plus的查询条件会拿不到正确的时间范围。缺陷提交表单我用了el-dialog嵌套el-form字段包括缺陷标题、所属项目下拉选择、严重级别单选、优先级单选、详细描述textarea、上传截图附件el-upload。提交前的表单校验规则不能省标题必填、项目必选、严重级别必选这些用el-form的rules配置即可。5.4 状态操作按钮的权限控制前端做权限控制的核心是v-if加角色判断。比如在缺陷列表的操作列需要根据当前用户的角色和缺陷当前状态决定显示哪些按钮el-table-column label操作 width240 template slot-scopescope !-- 详情按钮所有人都有 -- el-button sizemini typeprimary clickshowDetail(scope.row)详情/el-button !-- 指派按钮项目负责人且缺陷状态为新建 -- el-button v-ifrole 2 scope.row.status 1 sizemini typewarning clickshowAssignDialog(scope.row)指派/el-button !-- 修复按钮开发人员且被指派给自己状态为已指派 -- el-button v-ifrole 3 scope.row.assigneeId userId scope.row.status 2 sizemini typesuccess clickhandleFix(scope.row)修复/el-button !-- 验证按钮测试人员状态为已修复 -- el-button v-ifrole 4 scope.row.status 3 sizemini typeinfo clickhandleVerify(scope.row)验证/el-button !-- 关闭按钮项目负责人状态为已验证 -- el-button v-ifrole 2 scope.row.status 4 sizemini typedanger clickhandleClose(scope.row)关闭/el-button /template /el-table-column这里强调一遍前端按钮隐藏只是用户体验层面的处理后端接口必须做同样的权限校验否则绕过页面直接调接口就全完了。6. 常见问题与排查技巧实录6.1 跨域请求被拦截前后端分离项目第一个遇到的坑几乎都是跨域。前端跑在8080端口后端跑在8081端口浏览器会拦截跨域请求。解决办法有两种一是在后端加CORS配置类二是通过前端Vue CLI的proxy代理转发。我推荐第二种方案因为生产部署时前后端大概率会用Nginx做同域代理配置方式更接近真实场景。Vue CLI中在vue.config.js里配置代理module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true, pathRewrite: { ^/api: } } } } }这样前端请求/api/bug/list时会被代理转发到http://localhost:8081/bug/list因为路径重写把/api前缀去掉了。后端Controller里就不需要加/api前缀。如果后端也加CORS配置要注意是否和代理配置冲突一般二选一即可不要同时使用。6.2 分页数据查询始终返回全部数据这个问题常见于MyBatis-Plus的分页插件配置不正确。如果你发现调selectPage方法返回的是全表数据分页根本没生效那说明没有配置MyBatis-Plus的分页插件拦截器。需要在配置类中加入Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }这个配置不能漏漏了就分不了页。同时注意前端传参名MyBatis-Plus默认接收current和size两个参数而不是pageNum和pageSize。如果前端传的是pageNum后端接口需要手动转换为current。我在第一次联调时就在这里栽了跟头后端一直取不到分页参数。6.3 时间字段从数据库读出后相差8小时这是时区问题。MySQL连接串需要指定时区jdbc:mysql://localhost:3306/bug_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai同时后端在返回时间字段给前端时可以加统一配置让Jackson格式化spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8配置完这两个地方时间显示就正常了。如果还不够检查一下是不是前端在传日期时用了UTC字符串导致时间在转换时又被当作零时区偏移了。6.4 文件上传提示Tomcat连接超时图片太大或上传网速慢时可能触发Tomcat的连接超时。如果图片超过10MB建议在前端先做压缩或者适当调大后端的超时时间spring: servlet: multipart: max-file-size: 20MB max-request-size: 50MB server: tomcat: connection-timeout: 30000另外一个更隐蔽的坑本地开发时上传的图片路径是D:/upload/xxx.jpg但部署到Linux服务器后路径就失效了。所以上传路径最好配置化在application.yml里通过upload.path配置部署时改成服务器路径。同时要注意Linux下创建目录的权限权限不对会导致mkdirs()失败。6.5 前端路由懒加载报错用import()语法做路由懒加载时打包后会出现“chunk加载失败”的报错。这通常是因为旧版本浏览器不支持动态导入或者构建到生产环境后文件名带hash刷新后找不到对应的js文件。解决方案路由表中加了懒加载后构建时检查webpack配置Vue CLI 5默认已经支持code splitting一般没问题。如果确实报错可以降低浏览器兼容标准或者把懒加载改成常规的组件引用方式毕竟毕设系统页面数量有限不用为了性能牺牲稳定性。6.6 数据库连接数不足报错答辩演示时如果同时打开了多个页签后端可能出现Connection is not available, request timed out的错误。根因是连接池最大连接数太小。SpringBoot默认的HikariCP连接池最大连接数是10我建议加大一点spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5这个配置在演示场景下能有效避免并发请求打爆连接池。当然如果系统上线连接数的设置还要考虑数据库服务器本身的性能不是越大越好。7. 部署与扩展建议7.1 本地快速启动流程到这一步前后端代码基本就写完了。我总结一下本地跑通整个系统的流程方便你在写论文时参考这五大步骤创建数据库执行配置中提供的建库SQL创建bug_system数据库和全部数据表。启动后端修改application.yml中的数据库账号密码运行SpringBootApplication主类。启动前端在vue项目根目录执行npm install安装依赖再执行npm run serve。访问系统浏览器打开http://localhost:8080用预置的admin账号登录。测试核心流程创建一个项目再提交一条缺陷走一遍“提交→指派→修复→验证→关闭”完整流程。这里提醒一下数据库连接串里的数据库名要跟实际创建的库名一致账号密码也必须改成本地的MySQL信息这是第一次启动最常见的报错原因。7.2 从毕设到生产环境还差什么如果你想让这个项目不只是停留在毕设层面而是能真正在团队中用起来下面几个方向值得扩展用Redis保存JWT令牌和用户会话实现登录状态可注销和控制。现在JWT无状态用户改密码后旧token仍然有效存在安全隐患。增加缺陷导入导出功能支持Excel批量导入和导出方便和遗留系统迁移数据。增加邮件通知功能缺陷指派给开发人员时自动发邮件通知提高响应速度。在统计分析模块中增加缺陷趋势图用ECharts展示每日缺陷新增量和修复量这个图表在答辩演示时视觉效果很好。将前端构建后的文件部署到Nginx后端打包成Jar包部署到Linux服务器上并使用域名访问。这篇博文里已经包含了Linux相关的配置思路实际部署时要注意数据库和文件上传目录的路径问题。我自己做这个系统时最深刻的体会是毕业设计的技术实现只占一半另一半在于能不能把系统设计思路完整、有逻辑地讲出来。数据库表为什么这么设计状态流转为什么要加权限校验JWT相比Session有什么优势这些问题你如果能对着代码讲出个一二三那这套东西就真正内化成自己的能力了。最后再分享一个实用小技巧写论文或画图时用draw.io画一张系统功能结构图加一张数据库ER图答辩时往大屏幕上一放老师对你这个系统的整体认知会顺畅很多。本文还有配套的精品资源点击获取
返回列表