直接掏心窝子讲,这两年带了不少做毕业设计的学弟学妹,凡是选了管理系统这类题目的,十有八九最后都落在SpringBoot加Vue这套组合上。建筑材料管理系统这个题目,初看平平无奇,其实非常典型,该踩的坑一个不少,该用到的技术点也一个不缺——前后端分离、权限控制、多表关联、库存联动、报表统计,全都能在这个题目里找到合适的落点。无论你手里现在是有完整源码直接要改,还是准备从零开始搭一个,这篇文章都值得你花十分钟看完。我会把整个系统的设计思路、数据库怎么建、后端怎么写、前端怎么对接、毕业文档怎么组织,以及我实际调试过程中的各种坑,一次性都说清楚。
1. 项目整体设计:为什么选这套技术栈,功能到底怎么切
1.1 技术选型的底层逻辑
先说为什么市场上绝大多数毕业设计都选SpringBoot加Vue。这不是偶然,而是被就业市场和答辩老师共同逼出来的最优解。SpringBoot的核心价值在于它帮你把Spring MVC、Tomcat、Jackson这些基础组件全部内聚好了,你不需要再去写繁琐的XML配置,一个spring-boot-starter-web依赖丢进去,一个@SpringBootApplication注解标上,项目就能跑起来。对做毕业设计的学生来说,这意味着你可以在两三天内把后端骨架搭完,把精力集中在业务逻辑而不是环境配置上。
Vue这边同理,它的响应式数据绑定和组件化开发,让前端的表单校验、列表渲染、弹窗交互都变得非常直觉化。你用v-model绑定输入框,用axios发请求,用element-ui的表格组件直接渲染数据,二十多岁没写过几年代码的同学也能在短时间内做出一个能看的界面。
但选这套组合还有另一层考虑:答辩的时候老师几乎一定会问“为什么选这个技术栈”。SpringBoot加Vue的答案很好说——SpringBoot简化了后端开发流程,Vue实现了前后端分离,两者结合能降低维护成本、提升开发效率。这套话术老师听得多,也认可,不会在技术选型上继续追问太多。
建筑材料管理系统的业务场景其实非常适合这套技术栈。材料种类多、批次多、供应商多,天然就是增删改查的集中营。你做一个“材料信息管理”模块,做“供应商管理”模块,做“入库单管理”模块,做“出库单管理”模块,再把“库存预警”和“统计报表”加进去,整个系统的功能面就撑起来了。
1.2 功能模块怎么划分才合理
功能模块的划分直接决定你后面的代码量和数据库设计,一定要在动手之前想清楚。
我建议按这样的模块切分:
- 系统登录与用户管理:登录、退出、用户信息的增删改查,顺便加上密码加密存储
- 材料信息管理:材料的名称、规格型号、单位、分类、单价等基础信息的维护
- 供应商管理:供应商名称、联系人、联系电话、地址等信息的维护
- 材料入库管理:入库单的创建、审核,入库后自动增加对应材料的库存数量
- 材料出库管理:出库单的创建、审核,出库后自动扣减库存数量
- 库存管理:查询当前各种材料的库存数量、库存预警值,低于预警值自动标红
- 统计报表:按材料分类统计入库出库数量,用图表展示
这套模块划分的妙处在于:每个模块都是独立的增删改查,你可以单独开发单独测试;模块之间又存在清晰的业务关联,比如入库单会影响库存表,这为你的论文中“业务流程图”“数据流图”提供了很好的素材。
你注意“入库单管理”和“出库单管理”一定要设计成主表和从表的结构——主表记录这张单子的单号、操作时间、经办人,从表记录这张单子具体包含哪些材料、每种材料多少数量。这种设计在论文里可以大写特写“一对多关系建模”,在答辩时可以解释“为什么要拆两张表而不是直接存一个字段”,这些都是加分项。
2. 数据库设计的核心细节:表怎么建、字段怎么定、库存怎么联动
2.1 核心表单与字段全解
数据库是管理系统类毕业设计的命脉,也是答辩老师最喜欢盯着看的部分。建筑材料管理系统的核心表我建议至少包含以下七张:
用户表(sys_user):主键id、用户名、密码(BCrypt加密)、真实姓名、角色、创建时间。角色字段建议用字符串类型,直接区分“管理员”和“普通员工”,不要搞太复杂的RBAC模型,毕业设计阶段用不到。
材料分类表(material_category):主键id、分类名称、备注。这一张表用来对材料进行分类管理,简单实用。
材料信息表(material_info):主键id、材料名称、规格型号、单位、单价、分类id(外键关联material_category)、库存数量、库存预警值、备注。注意这里有一个冗余字段“库存数量”,这个字段是从出入库记录里统计出来的,但为了方便查询会直接存放在这张表里,这在论文里可以解释为“以空间换时间的设计”。
供应商表(supplier):主键id、供应商名称、联系人、联系电话、地址、备注。
入库主表(stock_in_main):主键id、入库单号、供应商id、入库日期、经办人、备注。入库单号建议用时间戳加随机数生成,比如“IN202501151430001”。
入库明细表(stock_in_detail):主键id、入库主表id(外键)、材料id、入库数量、单价。这张表里的单价要单独存,因为材料单价可能会调整,你不能去改历史入库记录里的价格。
出库主表(stock_out_main)和出库明细表(stock_out_detail):结构和入库表对应,字段基本一致,出库单号用“OUT”开头。
2.2 关键设计决策背后的原因
先说为什么“入库”和“出库”要拆主表和明细表。假设你入库单里包含三种材料,你当然可以设计成一张表,三个字段分别存材料名称、数量、单价,但如果一张单子有二十种材料呢?字段就爆炸了。更重要的是,主表明细表拆开之后,你可以通过外键关联,非常清晰地知道“这张单子包含哪些材料”,查询和统计都方便。这是一个非常经典的一对多模型,论文里的ER图画起来也好看。
再说“库存数量”为什么要冗余存储而不是每次临时计算。理论上库存数量等于总入库量减去总出库量,你可以通过SUM函数算出来。但如果你每次都去关联两张明细表做聚合运算,查询速度会越来越慢,而且当一个材料有几千条出入库明细时,这个查询会卡到你怀疑人生。把库存数量冗余在material_info表里,每次出入库操作完成后同步更新这个字段,查询列表页的时候一个简单的select就能把库存带出来,这就是典型的“空间换时间”。
这个设计在答辩时有个必考题:“如果两张明细表的数据被篡改或者删除,库存对不上怎么办?”你可以回答:通过事务机制保证数据一致性,同时增加对账逻辑,定期用SUM函数计算并与冗余字段比对,发现不一致时报警。这个回答会显得你有全局思维。
数据库的字符集最好统一设置为utf8mb4,排序规则用utf8mb4_general_ci。字段类型方面,金额字段一定用decimal而不是float或double,因为float在二进制存储上会有精度丢失,材料单价乘以数量后的总价会莫名其妙多出0.0000001元。数量字段根据你的材料类型决定,水泥你按吨存,用decimal(10,2)就够了。
2.3 建表SQL与索引优化思路
我直接把你建表SQL的核心部分给出来,你可以在自己的项目里照着改。
CREATE TABLE `material_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, `material_name` varchar(100) NOT NULL COMMENT '材料名称', `specification` varchar(100) DEFAULT NULL COMMENT '规格型号', `unit` varchar(20) DEFAULT NULL COMMENT '计量单位', `price` decimal(10,2) DEFAULT NULL COMMENT '单价', `category_id` int(11) DEFAULT NULL COMMENT '分类ID', `stock_quantity` decimal(10,2) DEFAULT '0.00' COMMENT '当前库存', `warning_value` decimal(10,2) DEFAULT '0.00' COMMENT '预警值', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_category_id` (`category_id`), KEY `idx_material_name` (`material_name`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='材料信息表';索引方面,你不需要建太多,主键索引是必须的,然后给外键字段建索引,比如category_id、material_id、supplier_id这些。因为这张明细表会频繁通过material_id去查询,没有索引的话,数据量一上来全表扫描会让你卡成PPT。至于材料名称,如果你要做模糊搜索,可以加一个普通索引,但不要指望模糊搜索能用上索引大幅加速,只要数据量在几千条以内,全表扫描也能接受。
3. 后端实现的核心环节:从工程搭建到接口联调
3.1 后端工程结构与启动配置
后端工程结构我建议严格遵循Controller、Service、Mapper三层,这不仅是业界惯例,更是论文里的“分层架构设计”章节的核心论据。你的包名建议这样组织:
com.example.material ├── controller (Controller层,接收前端请求) ├── service (Service层,编写业务逻辑) │ └── impl ├── mapper (Mapper层,操作数据库) ├── entity (实体类) ├── dto (数据传输对象) ├── vo (视图对象) ├── config (配置类,如跨域、拦截器) └── common (通用类,如返回结果封装、工具类)Controller层只负责接收参数、调用Service、返回结果,不写任何业务逻辑。Service层负责业务处理,比如入库操作要同时完成“插入入库主表、插入入库明细表、更新材料库存”三个动作,这三个操作必须放在同一个事务里,用@Transactional注解标注,任何一个失败都得回滚。Mapper层用MyBatis-Plus,你的单表增删改查完全不需要写SQL,直接调用BaseMapper提供的insert、selectPage、updateById方法就行。
启动类的写法非常固定,如下:
@SpringBootApplication @MapperScan("com.example.material.mapper") public class MaterialApplication { public static void main(String[] args) { SpringApplication.run(MaterialApplication.class, args); } }application.yml里要配置数据源、端口、MyBatis-Plus的相关参数。给你一个我实际用过的配置模板:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/material_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你自己数据库的密码 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0第一个坑提醒你:数据库连接url里务必加上serverTimezone=Asia/Shanghai,否则会出现时区报错。第二个坑:MyBatis-Plus的map-underscore-to-camel-case要打开,这样数据库字段create_time才能自动映射到实体类的createTime属性,不然查出来的对象时间字段全是null。
3.2 登录鉴权与拦截器处理
登录模块是每个答辩老师必问的内容。建筑材料管理系统的登录功能不建议搞太复杂,用JWT(JSON Web Token)方案就非常合适。我给你梳理一下流程。
用户登录接口:
@PostMapping("/login") public Result login(@RequestBody LoginDTO loginDTO) { // 根据用户名查询用户 User user = userService.getUserByUsername(loginDTO.getUsername()); // 判断用户是否存在 if (user == null) { return Result.error("用户名不存在"); } // 校验密码,PasswordEncoder是Spring Security自带的一个组件 if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) { return Result.error("密码错误"); } // 生成JWT,有效期设置24小时 String token = JwtUtil.createToken(user.getId(), user.getUsername()); // 返回给前端 return Result.success(token); }筛选拦截器:
public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { // 返回401状态码,前端会跳转到登录页 response.setStatus(401); return false; } // 解析token,如果异常直接拦截 try { Claims claims = JwtUtil.parseToken(token.substring(7)); // 可以向后续请求传递用户信息 request.setAttribute("userId", claims.get("userId")); } catch (Exception e) { response.setStatus(401); return false; } return true; } }注意你需要在WebConfig配置类里把拦截器注册进去,并且对放行的路径做出名单管理。比如登录接口/api/login肯定要放行,其他接口统一拦截。
前端配合的做法是在axios请求拦截器里给每个请求加上Authorization: Bearer xxx请求头,在响应拦截器里检测到401状态码就清空本地存储的token并跳转到登录页。这套方案实现简单、逻辑清楚,答辩时你从token生成、传递、校验三个环节分别讲解,老师会觉得你很扎实。
3.3 核心接口设计:材料入库出库与库存联动
材料入库这个接口是整个系统中业务逻辑最复杂的部分,写好了它你基本就建立信心了。我给你画一下它的完整执行轨迹:
前端提交入库单数据,数据形态大致如下:
{ "supplierId": 1, "remark": "本月第一次采购", "items": [ { "materialId": 1, "quantity": 100, "price": 12.5 }, { "materialId": 2, "quantity": 200, "price": 8.8 } ] }Service层处理逻辑:
@Transactional public void stockIn(StockInDTO dto) { // 1. 生成入库单号,格式如 IN + 时间戳 + 随机数 String orderNo = "IN" + System.currentTimeMillis() + RandomUtil.randomNumbers(4); // 2. 插入入库主表 StockInMain main = new StockInMain(); main.setOrderNo(orderNo); main.setSupplierId(dto.getSupplierId()); main.setStockInDate(new Date()); main.setRemark(dto.getRemark()); stockInMainMapper.insert(main); // 3. 遍历明细,插入入库明细表,并同时更新材料库存 for (StockInItem item : dto.getItems()) { StockInDetail detail = new StockInDetail(); detail.setMainId(main.getId()); detail.setMaterialId(item.getMaterialId()); detail.setQuantity(item.getQuantity()); detail.setPrice(item.getPrice()); stockInDetailMapper.insert(detail); // 更新库存 materialInfoMapper.increaseStock(item.getMaterialId(), item.getQuantity()); } }这段代码的精髓在@Transactional:如果循环到第三种材料时库存更新失败,前面插入的主表和前两条明细会全部回滚,数据库不会出现“半截单子”。你注意看这里用的是增加库存的方法,我推荐在Mapper里写这样一个自定义SQL:
UPDATE material_info SET stock_quantity = stock_quantity + #{quantity} WHERE id = #{materialId}直接用SQL操作原子增减,避免了先查询、再计算、再更新的三个步骤之间的并发问题。
出库的逻辑完全类似,只是把加号换成减号,并且要额外检查库存是否充足。出库前先查一下当前库存,如果库存不足直接抛异常提示“库存不足”,这个判断必须放在事务内部,否则会出现并发超卖的情况。
统计报表接口也比较简单。按月份统计入库数量,SQL大概是这样:
SELECT DATE_FORMAT(stock_in_date, '%Y-%m') AS month, SUM(quantity) AS total_quantity FROM stock_in_detail d JOIN stock_in_main m ON d.main_id = m.id WHERE m.stock_in_date >= #{startTime} AND m.stock_in_date <= #{endTime} GROUP BY month查询结果可以直接返回给前端,用ECharts画柱状图或者折线图。
4. 前端实现:Vue页面怎么搭、组件怎么用、请求怎么发
4.1 前端工程搭建与关键配置
前端我建议直接用Vue CLI或Vite来初始化项目。Vite现在启动速度快很多,如果你用的是Vue 3选Vite体验会非常好。如果你是JAVA课程里学的是Vue 2的语法,那你就用Vue CLI创建Vue 2项目,配上Element UI组件库,上手最快。
这里有一个非常常见的毕业设计现场问题:npm install安装速度慢到想摔电脑。解决办法是在项目根目录新建一个.npmrc文件,写入以下内容:
registry=https://registry.npmmirror.com这是国内镜像源,速度能快上十倍。如果你是校园网环境下开发,这个操作几乎是必须的,否则等一个晚上也可能装不完。
前端的目录结构我推荐这样建:
src ├── api (存放所有请求接口的调用方法) ├── assets (静态资源) ├── components (通用组件) ├── router (路由配置) ├── store (状态管理,可选) ├── views (页面级组件) ├── utils (工具类,axios封装) ├── App.vue └── main.js关于axios封装,一定要抽出公共请求入口。这里有一个跨域问题的关键点:如果你在vite.config.js里配置了代理,那么开发环境的请求路径要写成相对路径,由代理转发到后端;如果你懒得配代理,也可以在后端用@CrossOrigin注解处理跨域,但配代理更专业、更像真实项目里的做法。Vite的代理配置长这样:
server: { host: 'localhost', port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }这样前端请求/api/stock/list就会被转发到http://localhost:8080/api/stock/list,既解决了跨域,又不暴露后端真实地址。
4.2 页面设计与组件选型
材料管理系统的页面结构我非常建议用典型的“左侧菜单栏 + 右侧内容区”布局。Element UI的el-container组件可以直接实现这个布局,左侧放el-menu菜单,右侧放el-main内容区,内容区里通过路由切换展示对应的业务页面。
页面的核心组件是表格和数据弹窗。列表页用el-table绑定数据,每一行后面放“编辑”和“删除”按钮;新增和编辑功能统一用el-dialog弹窗,里面放el-form表单。
材料列表页的核心代码逻辑大致如下:
<template> <div> <el-form inline> <el-form-item label="材料名称"> <el-input v-model="queryParams.materialName" placeholder="请输入材料名称"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery">查询</el-button> <el-button type="success" @click="handleAdd">新增</el-button> </el-form-item> </el-form> <el-table :data="tableData" border stripe> <el-table-column prop="materialName" label="材料名称"></el-table-column> <el-table-column prop="specification" label="规格型号"></el-table-column> <el-table-column prop="price" label="单价"></el-table-column> <el-table-column prop="stockQuantity" label="库存数量"></el-table-column> <el-table-column label="操作" width="200"> <template slot-scope="scope"> <el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button> <el-button type="danger" size="small" @click="handleDelete(scope.row)">删除</el-button> </template> </el-table-column> </el-table> <el-pagination :current-page="queryParams.current" :page-size="queryParams.size" :total="total" @current-change="handlePageChange"></el-pagination> </div> </template>这里要注意,表格的:data来自一个tableData数组,通过调用api方法获取后端返回值后赋值。分页组件要处理好current-change事件,切换页码时重新加载数据。
库存预警页面的实现更有意思,核心逻辑是当前端从后端拿到材料列表后,前端做一次过滤,把库存数量低于预警值的材料筛出来,并且在表格中用红色高亮显示。代码大致是这样:
computed: { warningList() { return this.tableData.filter(item => Number(item.stockQuantity) < Number(item.warningValue) ) } }在el-table的行样式上可以绑定一个函数,让预警的行标红。这个功能虽然简单,但在答辩演示时效果非常好——你现场把某种材料的库存调低,点击刷新,表格立刻变红,老师一眼就能看到系统的作用。
4.3 前后端联调的接口对接经验
前后端联调阶段,我强烈建议你先在后端用Postman把所有接口都测通了,再开始对接前端页面。这样出问题时你能快速定位是后端接口的问题还是前端传参的问题。
联调时最常见的错误是字段名对不上。比如数据库字段是stock_quantity,后端的实体属性是stockQuantity,前端代码里用了stock_quantity,结果页面始终显示不出库存数量。排查这类问题的方法是打开浏览器的控制台Network面板,看接口实际返回的JSON字段长什么样,再对照前端代码里的取值属性名,一目了然。
还有一个高频坑是时间字段的传输。后端返回的Date类型,默认序列化成一串很长的数字时间戳,前端拿到之后需要格式化才能显示成2025-01-15。你可以在后端给时间字段加上@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")注解,或者在前端写过滤器做格式化。推荐在后端处理,因为前端每个页面都要显示时间,统一处理省事得多。
5. 毕业设计文档撰写与答辩的核心策略
5.1 论文的章节结构与写作要点
如果你手里的交付物包含“文档”,那你一定逃不掉写毕业设计论文或者开发文档。这个文档的章节结构是有约定俗成模板的,你按下面的骨架填充就不会跑偏。
第一章绪论:写研究背景和意义。这里可以写“建筑行业信息化管理需求日益增长,传统人工记录材料出入库信息的方式存在效率低、易出错、统计困难等问题,因此开发一套建筑材料管理系统具有实际应用价值”。再写国内外研究现状和主要工作。
第二章需求分析:写系统的功能需求和非功能需求。功能需求用用例图辅助说明,角色就设两个——管理员和普通员工。
第三章总体设计:写系统架构图、功能模块图、技术选型、数据库设计。数据库设计这里放ER图和建表SQL。
第四章详细设计与实现:逐模块写实现过程。从类设计到核心代码,每个模块配一个页面截图。这一章是字数主力,你必须把登录、材料管理、库存管理、出入库管理、统计报表六个模块全部覆盖,每个模块写清流程和核心方法。
第五章系统测试:写测试用例表和测试结论。用黑盒测试方法,列出功能测试用例,说明预期结果和实际结果一致。
这里有一个毕业论文的隐藏要求:如果你的学校查重要求比较严格,那么核心代码部分最好改成流程图或者直接用文字描述流程,不要大段大段贴代码,否则重复率会非常感人。数据库建表SQL也不要全贴,只贴核心表并配上字段说明表。
5.2 答辩必问问题与标准回答
答辩老师对管理系统类题目的提问模式其实非常固定,提前把答案准备充分,现场就不会慌。
第一个必问:系统采用了什么架构?答案要讲到前后端分离、SpringBoot负责后端接口、Vue负责页面渲染、两者通过JSON进行数据交互。你可以多提一句“这种架构提高了开发效率,降低了前后端耦合度”,这个回答是标准加分项。
第二个必问:数据库中的库存数据是怎么保证准确的?你要把前面说的主表明细表事务设计讲清楚——每次入库出库操作都在同一个事务中执行,要么全部成功要么全部回滚,同时更新库存冗余字段。再补充一句“系统还支持定期对账,用SQL统计出入库总量和库存字段进行比对”。这句话能体现你在设计时考虑到了数据一致性。
第三个必问:系统的安全性体现在哪里?你可以讲密码通过BCrypt加密存储,登录采用JWT鉴权,未登录用户无法访问受保护接口,前端通过路由守卫控制页面访问权限。这三个点分别对应后端安全、接口安全、前端安全,一石三鸟。
第四个必问:未来可以怎么优化?这个问题考察你的延伸思考能力。你可以说三个方向:一是引入Redis缓存热点数据,减轻数据库压力;二是使用消息队列处理出入库通知,提升系统吞吐量;三是增加多维度数据可视化分析,帮助管理者优化采购决策。每一条都点到即止,不要展开太多,说多了容易露怯。
6. 常见问题与调试实录:我亲自踩过的坑
6.1 环境配置阶段的经典事故
环境问题是最劝退毕业生的,我筛选出几个高频事故,集中在第一次运行项目时的注意事项。
数据库连接失败是非常典型的。现象是启动SpringBoot时控制台疯狂报错,类似Access denied for user 'root'@'localhost'。排查思路非常简单:先检查MySQL服务有没有启动,任务管理器里找mysqld.exe进程;接着检查application.yml里的用户名密码是否和本机一致。绝大多数时候,不是你密码错了,而是你的MySQL密码就是空的,或者密码变了但你yml里没更新。
端口占用也很常见。SpringBoot启动时提示Port 8080 was already in use。解决办法:要么换端口,把server.port改成8081;要么找到占用进程并结束它。命令行执行:
netstat -ano | findstr 8080 taskkill /pid 对应PID /fMaven依赖下载失败也困扰了无数人。现象是pom.xml里引入了spring-boot-starter-web,但是mvn编译时提示找不到类或者Jar包下载报错。根本原因是默认的Maven中央仓库地址在国内访问极不稳定。解决办法是在Maven的settings.xml文件里配置阿里云镜像:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>配好之后把本地仓库里失败的存档清掉,重新执行mvn clean install就可以解决。
6.2 开发阶段几个防不胜防的Bug
MyBatis-Plus的分页失效问题可能让你怀疑人生。你以为加了PageHelper或者Page类就能分页,结果发现查出来的数据列表是全量的。原因是你可能没有配置分页插件。在MyBatis-Plus中,分页需要显式注入一个插件:
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }没有这个配置类,selectPage方法虽然不会报错,但会直接把全表数据都查出来,你前端表格能显示数据,翻页却没任何效果。这个问题特别隐蔽,答辩前不仔细测试根本发现不了。
还有一个特别气人的是前端联调时的缓存问题。你改了前端代码,以为刷新浏览器就能看到新效果,结果页面还是旧的。多半是浏览器缓存了静态文件。解决办法是强制刷新(Ctrl+F5),或者在Vite配置里禁用缓存。如果是部署到服务器上出现这个问题,还可以考虑给打包后的JS文件名加hash值。
后端修改了接口、前端请求之后得到的数据仍然不变,这个通常是后端代码没有重新启动。SpringBoot默认不会热部署,你得手动重启或者配置DevTools热部署插件。DevTools配置很简单,pom里加这个依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>加了之后,修改Java代码并保存,项目会自动重启,调试效率能提升一大截。
6.3 部署阶段的注意事项
有的学校毕业答辩要求本地演示,有的要求部署到服务器。如果你需要打包部署,我建议直接用Maven打包成可执行Jar包:
mvn clean package完成之后,target目录下会生成一个.jar文件,你可以用这个命令启动后端:
java -jar material-system.jar前端打包则执行:
npm run build它会生成一个dist目录,里面是静态文件。如果你想在服务器上同时部署前后端,推荐用Nginx。前端dist目录放到Nginx的html目录下,后端Jar包用java -jar启动,再把Nginx的反向代理配置一下,让请求/api路径时转发到后端8080端口。这样整个系统就能通过一个域名或IP访问,非常专业。
Nginx的配置如下:
server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里try_files的作用是解决前端路由的刷新404问题——如果你的页面是/stock/list这种路由,直接刷新浏览器时Nginx会找不到对应的静态文件,加上这句配置,请求都会兜底到index.html,由Vue Router接管路由分配。
从技术栈选型到数据库设计,从后端事务处理到前端页面联调,从论文结构到答辩话术,再到部署方案,这套建筑材料管理系统能涉及到的知识点我基本全部覆盖了。做毕业设计这几年,我自己的体会是:不要把毕业设计当成任务交差,而是当成一个小型项目去认真完成。这个过程中你学会的排查Bug的思路、前后端协作的模式、文档撰写的逻辑,到了工作岗位上都是每天要用的基本功。最后再送你一个小建议——做完一个模块就及时提交一次代码,养成用Git做版本管理的习惯,你一定会感谢自己这个决定。