☰
供应商管理系统毕设实战:SpringBoot+Vue全栈开发指南
2026/10/2 21:50:03 网站建设 项目流程

1. 这个项目到底在解决什么问题:毕设选题与需求拆解

先说个好多人容易忽略的点:毕设选题决定了你后面大半年是舒服还是折磨。我见过太多人选了那种"智能推荐系统""基于深度学习的什么什么检测",一听很高端,结果数据凑不齐、模型跑不动、导师一问原理就答不上来,最后开题时吹的牛全变成答辩时的坑。

供应商管理系统这个选题好在哪里?它是典型的企业管理类Web系统,需求清晰、边界明确、功能可量化,而且覆盖了Java Web毕设该有的所有核心考点:CRUD、关联表设计、分页查询、文件上传、权限控制、报表统计、多条件筛选。这种项目不需要玄学算法撑场面,只要功能完整、流程顺畅、接口规范,就能拿一个体面的分数。

那"供应商管理系统"到底是管什么的?用大白话说,一个企业要采购原材料或者服务,不可能满世界随便找供应商,总要有个系统来登记谁有资质、谁报价低、谁交付及时、谁的合同快到期了。这个系统围绕供应商的全生命周期来做功能:准入时登记基础信息,合作中记录报价和订单,事后做绩效评价,最后形成一条完整的管理闭环。

我拿到需求后第一件事不是写代码,而是把角色和模块拆清楚。这套系统的用户分三类:

  • 管理员:负责供应商准入审核、用户管理、系统配置、数据统计。
  • 采购员:检索供应商、发起询价、创建采购订单、提交收货记录。
  • 供应商(如果做成多端的话):维护自己的资料、上传资质、查看订单状态。不过很多毕设版本会把供应商侧简化成纯数据被动录入,由管理员或采购员代为维护,这个取舍跟你的项目周期直接相关。

我建议第一次做这个题目的人,功能列表控制在以下范围就够了:

  • 供应商档案管理(增删改查、多条件搜索、状态流转)
  • 资质证照管理(上传文件、有效期预警)
  • 产品与报价管理(每种供应商能供什么货、最新报价是多少)
  • 采购订单管理(从订单生成到收货完成的流程跟踪)
  • 供应商评价管理(按交付质量、价格、服务打分)
  • 系统管理(用户、角色、菜单权限)
  • 数据仪表盘(供应商数量统计、品类分布、订单趋势)

这个范围既撑得起"平台"两个字,又不会让开发量失控。后面写代码时你会感谢自己当初没有再加什么"供应商协同工作流引擎""智能价格预测",那种需求属于给自己加戏。

2. 技术选型背后的真实考虑:为什么是SpringBoot+Vue

这个题的标题已经把技术栈钉死了,但作为过来人,我还是想聊聊为什么这套组合适合毕设,以及你在开题报告和答辩PPT里怎么把选型动机讲得有理有据。这比代码本身更能让导师觉得你思路清楚。

2.1 后端选SpringBoot的真实理由

SpringBoot不是"因为大家都在用所以我也用",它的核心优势对毕设这种短周期项目来说几乎是量身定制的。

首先是起步成本低。传统SSH或者SSM那套,光配置文件就能劝退一批人,Spring自动配置机制把这些东西全兜住了。你创建一个SpringBoot项目,引入spring-boot-starter-web,写个@RestController,就能跑起一个Web服务,前后不超过十分钟。这对毕设这种"从零到演示往往只有两三个月的冲刺时间"的场景极其重要。

其次是生态整合省心。连接MySQL用spring-boot-starter-jdbc或者MyBatis,做权限用Spring Security或者Sa-Token,文件上传就是MultipartFile,导出Excel有EasyExcel,几乎每个需求都能找到现成的starter,不用自己去造轮子。我的经验是:毕设阶段千万不要自己封装底层框架,时间耗不起,而且答辩时容易被问住。

第三是自带容器、部署方便。SpringBoot内置Tomcat,打包成可执行的Jar,java -jar就完了,不需要单独去装配置Tomcat,这对最后演示环境搭建来说是巨大的减压。后面我会讲部署细节,现在你记住这个结论就行。

2.2 前端选Vue的理由和版本差异

Vue在国内Java Web毕设圈的地位不用多说,关键是你得选对版本。现在网上很多老教程还在讲Vue 2,你用Vue 2也能做,但我想认真给你一个建议:如果是从零开始做毕设,用Vue 2还是Vue 3要看你的水平和时间。

  • 如果你之前只在学校学过Vue 2,网上项目源码也大多是Vue 2的,那就别纠结,直接撸Vue 2 + Element UI。为什么?因为毕设最重要的是在规定时间跑通,不是秀你的技术前瞻性。
  • 如果你已经有Vue 3的基础,或者愿意花两天时间看文档,那Vue 3 + Vite + Element Plus是更现代的选择,打包速度、组合式API的代码组织方式都很舒服。

我这个项目用的版本是Vue 2 + Element UI,原因特别现实:网上的免费后台管理模板、现成组件示例、各种答疑帖子最多的是这个组合,碰到问题搜起来效率最高。你要是选了Vue 3遇到一个冷门报错,搜遍全网都找不到解法,那种痛苦比版本老一点难受多了。

前端这块,后台管理系统的页面其实高度模式化:左侧菜单、顶部面包屑、中间内容区放表格和表单。Vue的组件化开发正好把这种模式拆得很干净,后面我会具体讲页面结构怎么搭。这里先记住一句话:选型不是比参数,是比"我能不能在有限时间内交付一个能稳定演示的程序"。

3. 数据库表结构设计:供应商管理系统的核心实体关系

数据库设计是整个项目的地基,地基歪了后面写什么代码都别扭。我在做这个项目时,表结构前后调了三版,第一版按"供应商-产品-订单"三张表硬啃,结果发现资质证照、评价记录这些数据无处安放,第二版才把实体关系理顺。给大家看看最终版本的设计思路,这是源码里SQL脚本的核心。

3.1 核心表清单与设计意图

我先列一张总表,让你对整体有个概念,然后再拆重点表说明。

表名作用关键字段关联关系
sys_user系统用户id, username, password, real_name, role_id关联角色表
sys_role角色id, role_name, role_key被用户表引用
supplier供应商主表id, supplier_code, name, credit_code, status被多张业务表引用
supplier_contact供应商联系人id, supplier_id, name, phone多对一供应商
supplier_qualification资质证照id, supplier_id, cert_name, cert_no, expire_date多对一供应商
product_category产品品类id, category_name, parent_id自关联树形
product产品id, product_name, category_id, spec关联品类
supplier_product供应商供应产品id, supplier_id, product_id, price多对多中间表
purchase_order采购订单主表id, order_no, supplier_id, total_amount, status关联供应商
purchase_order_item订单明细id, order_id, product_id, quantity, unit_price一对多订单
supplier_evaluation供应商评价id, supplier_id, score, eval_content, eval_time多对一供应商

3.2 供应商主表的设计细节

supplier表是整个系统的数据中枢,我见过很多人把联系人、电话、地址全堆在这一张表里,结果一个供应商有多个联系人时就傻眼了。正确的做法是主表只存供应商不可变的身份信息,变化的、多值的信息拆到子表。

CREATE TABLE `supplier` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `supplier_code` varchar(32) NOT NULL COMMENT '供应商编码', `name` varchar(128) NOT NULL COMMENT '供应商名称', `credit_code` varchar(64) DEFAULT NULL COMMENT '统一社会信用代码', `legal_person` varchar(64) DEFAULT NULL COMMENT '法人代表', `address` varchar(255) DEFAULT NULL COMMENT '注册地址', `category_id` bigint(20) DEFAULT NULL COMMENT '主营品类', `status` tinyint(4) DEFAULT '0' COMMENT '状态:0待审核 1合作中 2已停用', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_supplier_code` (`supplier_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商主表';

这里有两个容易踩坑的点:

  • supplier_code必须唯一且手动生成,不要用自动递增充当代码,因为代码会有业务含义(比如用日期加序号生成),而且后续导入导出时唯一标识不会乱。
  • 状态字段用tinyint而不是varchar,0待审核、1合作中、2已停用、3已黑名单,这个在代码里用枚举去对应。为什么不用字符串?因为数据库里存字符串容易大小写不一致、空格混入,查问题查到头皮发麻,用数字虽然语义不够直观,但配合代码注释和文档,反而是最稳的。

3.3 资质证照表和有效期预警的设计

供应商的资质证照是审核的关键依据,这张表设计的亮点在于过期提醒功能。表里有expire_date字段,业务层每次查询时对比当前日期,把"已过期"和"30天内到期"的记录标记出来,前端列表里做醒目的红色和黄色标签。这个功能看起来小,但在答辩演示时效果极好——导师会直观感受到"这个系统在主动替用户着想",而不只是数据堆积。

CREATE TABLE `supplier_qualification` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `supplier_id` bigint(20) NOT NULL COMMENT '供应商ID', `cert_name` varchar(128) NOT NULL COMMENT '证书名称', `cert_no` varchar(128) DEFAULT NULL COMMENT '证书编号', `expire_date` date DEFAULT NULL COMMENT '有效期至', `file_url` varchar(255) DEFAULT NULL COMMENT '证照文件路径', `remark` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_supplier` (`supplier_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供应商资质证照表';

file_url字段直接存文件访问路径,文件本身上传到本地磁盘目录(有条件的可以接MinIO对象存储,后面我会讲这个可扩展点)。毕设阶段不建议搞分布式存储,本地目录挂一个虚拟路径映射就够了,这点在第6部分部署时会细说。

3.4 多对多关系的中间表设计

供应商和产品之间是多对多关系,一个供应商可以供应多种产品,一个产品也可能有多个供应商供货。所以必须有supplier_product中间表,而且这张表上还要带价格字段,因为"某供应商对某产品的报价"是业务上要记录的关键数据。这就在多对多基础上扩展出了业务属性,是数据建模里很经典的用法。

同样,采购订单主表purchase_order和订单明细purchase_order_item是一对多的父子结构。为什么订单要拆两张表?因为一张订单里可能有多个产品,每行明细有各自的数量和单价;如果只做一张表,那同一个订单号要重复很多行,主表的订单金额就不知道放哪里好了。拆开之后,主表存订单号、供应商、总金额、状态,明细表存每一行的货物明细,主表和明细通过order_id关联,逻辑清爽、统计方便。

这个表的拆分逻辑,建议在论文的数据库设计章节大幅写一下,很容易展现实力。

4. 后端接口设计与实现:从Controller到业务层的完整落地

后端的实现质量直接决定系统稳不稳。这个项目的后端我建议按经典的分层结构来:Controller接收请求、Service处理业务、Mapper操作数据库。下面把接口设计规范和核心业务链路拆开讲。

4.1 统一接口规范:返回体与异常处理

先定一个统一返回体,这是项目里所有接口的"外壳":

{ "code": 200, "message": "操作成功", "data": { } }

code=200表示成功,其他值表示业务异常;message携带具体提示;data是业务数据。Java后端对应一个泛型类Result<T>,所有Controller方法都返回它。这样做的好处是前端Axios拦截器一套统一处理逻辑,不需要每个接口单独判断。

异常处理也是一个必须写好的点。数据校验失败、供应商不存在、订单状态不能变更,这些都属于业务异常。项目里我定义了一个BizException,在Service层遇到问题就throw new BizException("xxx"),再配一个@RestControllerAdvice全局异常处理器统一捕获。这样做代码里就没有一堆try-catch嵌套了,逻辑读起来干净很多。

4.2 核心模块的接口设计清单

我把主要接口列成一张表,你对照着看就知道一个完整的供应商管理系统需要覆盖哪些端点。这张表同时也是你写接口文档时的目录骨架。

功能模块请求方法路径说明
登录认证POST/api/auth/login登录,返回Token
供应商管理GET/api/suppliers分页+多条件查询
供应商管理POST/api/suppliers新增供应商
供应商管理PUT/api/suppliers/{id}修改供应商
供应商管理PUT/api/suppliers/{id}/status变更状态(审核/停用)
资质证照POST/api/suppliers/{id}/qualifications添加资质
资质证照GET/api/suppliers/{id}/qualifications查看资质列表
产品报价POST/api/supplier-products配置某供应商供应产品及报价
产品报价GET/api/suppliers/{id}/products查询某供应商的产品列表
订单管理POST/api/orders创建采购订单
订单管理GET/api/orders分页查询订单
订单管理PUT/api/orders/{id}/receive确认收货
供应商评价POST/api/evaluations新增评价
供应商评价GET/api/evaluations/supplier/{id}查询某供应商的评价记录
数据统计GET/api/dashboard/summary供应商数量、订单趋势等统计
文件上传POST/api/upload上传资质证照文件
用户管理GET/POST/PUT/api/users系统用户的增删改查

说一个很多毕设源码里常见的偷懒写法:把多个操作揉进一个接口里。比如"供应商状态审核"功能,有人就直接用PUT /api/suppliers传个对象,把状态字段一起改了,这样看起来节省了接口数量,但审计追溯很麻烦。正规做法是单独的路径加status参数,并且把操作日志记录下来。项目里我把状态变更加了专门的接口,旁边还加了一行记录是否审批通过、审批人是谁,这部分细节答辩时很加分。

4.3 供应商分页查询的后端实现逻辑

供应商列表是系统里用得最多的功能,没有之一。它需要支持按供应商名称模糊搜索、按状态筛选、按品类筛选、分页展示。这个接口看起来简单,但背后有几点值得展开:

Controller层用参数对象接收查询条件:

@GetMapping("/suppliers") public Result<PageResult<SupplierVO>> page(SupplierQuery query) { return Result.success(supplierService.pageSuppliers(query)); }

SupplierQuery里包含pageNum、pageSize、keyword、status、categoryId这几个字段。Service层拿到参数后构造MyBatis的QueryWrapper或者XML里的动态SQL,核心是动态条件拼接不能用字符串拼SQL,防止SQL注入。

这个是MyBatis-Plus的写法,条件构造器会自动忽略null值条件:

LambdaQueryWrapper<Supplier> wrapper = Wrappers.lambdaQuery(); wrapper.like(StringUtils.isNotBlank(query.getKeyword()), Supplier::getName, query.getKeyword()); wrapper.eq(query.getStatus() != null, Supplier::getStatus, query.getStatus());

分页用MyBatis-Plus的Page<Supplier>配合selectPage,一步到位。然后把查出的实体列表转成VO返回给前端,VO里把categoryId翻译成品类名称、把status翻译成中文状态,这些翻译动作放在Service层做,不要让前端来猜。

4.4 采购订单状态流转的实现

订单状态是整个系统里最有业务感的逻辑,它有一个明确的状态机:草稿(0) -> 已下单(1) -> 已收货(2) -> 已完成(3) -> 已取消(4)。

创建订单时状态为草稿,然后确认下单后变为已下单;供应商发货后手动确认收货变为已收货;全部流程走完进入已完成。这里最容易出的逻辑漏洞是乱序流转,比如直接把已取消的订单改成已完成。在Service层写状态流转时必须做前置校验:

// 状态流转校验 OrderStatus current = order.getStatus(); if (current == OrderStatus.CANCELLED) { throw new BizException("已取消的订单不能继续流转"); }

这是状态机设计里很关键的处理方式,代码量不大,但能挡住80%的非法操作。答辩时如果被问到"多个用户同时操作同一个订单怎么办",你可以说:把状态更新写成SQL条件更新,WHERE id=? AND status=?,这样即使两个请求同时进来,也只有一个能更新成功。这个回答的含金量非常高,因为它是真正在分布式并发场景下思考过的体现。

4.5 文件上传与静态资源映射

资质证照文件上传,SpringBoot的MultipartFile接住后写到一个本地目录,比如/data/upload,然后返回访问路径/uploads/xxx.pdf。为了让前端能访问到这个目录,需要配置虚拟路径映射:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 将 /uploads/** 映射到磁盘 /data/upload/ 目录 registry.addResourceHandler("/uploads/**") .addResourceLocations("file:/data/upload/"); } }

这个配置是毕设里必考的细节,很多人的文件上传功能本地能用、部署到服务器就404,十有八九就是忘了配置资源映射器。我在部署章节会再提一次。

扩展点说一下:如果你觉得本地存储太low,想给系统加分,可以接MinIO对象存储。minio加入到springboot也是最近很多人搜的关键词。MinIO是一个兼容S3协议的开源对象存储服务,SpringBoot里引入依赖,配置endpoint、accessKey、secretKey、bucketName,调用MinioClient.putObject就能把文件传到对象存储。这也是我很推荐的一个加分点,因为它在答辩时可以讲"文件存储与业务解耦,支持后续水平扩展"。

5. 前端页面与数据联调:Vue后台管理的工程化细节

后端接口完成后,前端就是一个把接口数据填充进页面的过程,但工程化之后还是有大量细节。前端代码组织我建议按"页面-路由-组件-API"四个层次来拆。

5.1 前端项目结构的模块划分

一个典型的Vue后台管理前端目录结构:

src/ ├── api/ # 接口调用封装 │ ├── login.js │ ├── supplier.js │ └── order.js ├── assets/ # 静态资源 ├── components/ # 公共组件 │ ├── Pagination.vue │ └── UploadFile.vue ├── router/ # 路由配置 ├── store/ # 状态管理(Vuex) ├── views/ # 页面 │ ├── login/ │ ├── supplier/ │ ├── order/ │ ├── evaluation/ │ └── dashboard/ └── utils/ # 工具函数 ├── request.js # Axios封装 └── auth.js # Token存取

用views和components分隔的意义在于:views是按路由维度组织的页面,components是按复用维度组织的通用零件。比如供应商列表、联系人弹窗、资质上传这些页面组件之间,一定会共用分页组件、状态标签组件、上传组件,所以把通用逻辑抽到components里是省时间的关键。

5.2 Axios封装:拦截器与统一错误处理

前端所有接口调用都走统一封装的Axios实例。这个封装要做三件事:

// utils/request.js import axios from 'axios' import { Message } from 'element-ui' import router from '@/router' const service = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器:加上Token service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config }) // 响应拦截器:统一处理后端返回的code service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { Message.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res.data }, error => { Message.error(error.message || '网络异常') return Promise.reject(error) } )

响应拦截器里判断code并弹出后端返回的message,意味着前端每个API调用就只需要写请求本身,错误处理全是自动的。这里有个容易被忽略的细节:不要把整个Response返回,直接返回res.data,让业务代码拿到的就是后端数据本体,代码会清爽非常多。

5.3 Element UI表格页面的标准写法

以供应商列表页为例,标准的四段式:搜索区、按钮区、表格区、分页区。搜索区是一个el-form内联表单,表格区是el-table绑定数据数组,分页区是el-pagination。这里我分享一个写表格页面的小套路:

<template> <div class="app-container"> <!-- 搜索区域 --> <el-form :inline="true" :model="query"> <el-form-item label="供应商名称"> <el-input v-model="query.keyword" placeholder="请输入名称" clearable /> </el-form-item> <el-form-item label="状态"> <el-select v-model="query.status" placeholder="全部"> <el-option label="待审核" :value="0" /> <el-option label="合作中" :value="1" /> <el-option label="已停用" :value="2" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="loadData">查询</el-button> </el-form-item> </el-form> <!-- 表格区域 --> <el-table :data="list" border stripe> <el-table-column prop="supplierCode" label="编码" width="120" /> <el-table-column prop="name" label="供应商名称" /> <el-table-column prop="categoryName" label="主营品类" /> <el-table-column label="状态"> <template slot-scope="{ row }"> <el-tag :type="statusType[row.status]">{{ statusText[row.status] }}</el-tag> </template> </el-table-column> <el-table-column prop="createTime" label="创建时间" width="170" /> <el-table-column label="操作" width="220" fixed="right"> <template slot-scope="{ row }"> <el-button type="text" @click="viewDetail(row)">详情</el-button> <el-button type="text" @click="handleEdit(row)">编辑</el-button> <el-button type="text" @click="handleDelete(row)">删除</el-button> </template> </el-table-column> </el-table> <!-- 分页 --> <el-pagination background layout="total, prev, pager, next, sizes" :total="total" :page-size="query.pageSize" @current-change="handlePageChange" @size-change="handleSizeChange" /> </div> </template>

这段代码里有两个细节说一下。一是状态列用el-tag加动态类型,让不同状态变色,体验感直接上一个台阶;二是操作列加fixed="right",当表格横向滚动时操作按钮始终可见,这个交互细节导师演示时很容易注意到。

5.4 前端路由与权限控制

Vue Router配置里,登录页和首页分开,业务页面全部挂在Layout布局组件下作为子路由。权限控制的简单做法是在前端路由配置的meta字段里加roles,比如供应商管理页面仅管理员可见。不过更真实的做法是:后端返回该用户可访问的菜单列表,前端根据它动态生成路由,这就是很多人搜的"vue动态路由"。

动态路由实现起来不难:登录成功后请求/api/auth/menus,拿到当前角色可见的菜单数组,用router.addRoutes动态添加。这样做还有个额外的好处是侧边栏菜单也不用写死了,直接根据菜单数据循环渲染。权限控制这块在毕设里属于"加分项",如果你时间不够,先做好按钮级别的权限就够了。

6. 从源码到可演示:本地环境部署全流程与避坑指南

不管代码写得多漂亮,最后导师要看的是能跑起来的演示。这一章我把整个部署流程从头到尾捋一遍,全是实操中容易卡住的地方。我拿到的这套源码配套了SQL脚本和接口文档,正好可以按标准流程走。

6.1 环境准备清单

先把环境版本定下来,避免后面各种不兼容问题:

软件推荐版本说明
JDK1.8 或 11别用太高版本(如JDK 21),某些老依赖不兼容
Maven3.6.x 或 3.8.x配置阿里云镜像加速下载
MySQL5.7 或 8.0注意8.0的驱动名变化
Node.js14.x 或 16.xVue 2项目建议16,Vue 3 + Vite建议18+
npm/yarn随Node建议指定淘宝镜像源

这里重点说两个坑:

  • SpringBoot版本太高是个常见问题,很多项目的代码是基于SpringBoot 2.x写的,你非要用3.x测,会发现一堆javax和jakarta包名不兼容的错误。拿到源码先看pom.xml里的parent版本,如果它写的是2.x,就老老实实用JDK 8。
  • MySQL 8和MySQL 5.7的驱动差异:驱动名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver,而且8.x要求配置时区参数serverTimezone=Asia/Shanghai。修改application.yml时注意区分。

6.2 导入SQL脚本的正确顺序

项目提供了SQL脚本,但很多人导入时会踩坑。正确步骤是:

  1. 先在MySQL里创建数据库:CREATE DATABASE supplier_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
  2. 选择数据库:USE supplier_db;
  3. 执行SQL脚本:source /path/to/init.sql;(命令行方式)或用Navicat直接运行SQL文件。

这个顺序有个关键点:先建库再跑脚本。如果SQL脚本里没有CREATE DATABASE语句而你又直接运行,会报"没有选择数据库"的错误。还有字符集一定要用utf8mb4,因为它能存储emoji和生僻字,utf8在MySQL里是不够用的。

导入完成后检查一下数据,正常情况下应该有管理员账号(比如admin/admin123)、若干个测试供应商、几笔订单数据。强烈建议别清空这些测试数据,演示时直接有内容看比现场造数据体面多了。

6.3 后端启动流程与常见报错

后端配置集中在application.yml里。需要修改的无非是数据源:

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/supplier_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你自己的密码 servlet: multipart: max-file-size: 50MB max-request-size: 50MB

multipart配置是文件上传功能必须的,不配的话默认1MB,传个资质证书图片都可能会报错。启动时用IDE直接跑主类,或者打包后跑java -jar xxx.jar。常见报错和排查思路:

  • Access denied for user:用户名密码错误,或者权限不够。
  • Unknown database:数据库没建成功。
  • Port was already in use:8080端口被占用,改server.port。
  • Failed to configure a DataSource:数据源配置没生效,检查application.yml的缩进层级。

Failed to configure a DataSource这个报错很阴险,它不一定是你配置写错了,而可能是配置文件根本没被加载。检查方法:看启动日志里有没有加载配置的提示,或者在application.yml目录确认一下文件名是不是拼错了(application不是applications)。

6.4 前端启动与联调

前端启动前先装依赖。强烈建议用npm并指定镜像源:

npm install --registry=https://registry.npmmirror.com npm run dev

npm install慢或者报错是新手最常卡住的地方。如果一直卡住,用淘宝镜像;如果node-sass安装失败(这个东西是Vue 2项目的常客),尝试用npm install -g node-sass --registry=https://registry.npmmirror.com,或者在项目里换dart-sass替代,后者是更省心的选择。

前端跑起来后会有个关键问题:跨域。前端开发服务器默认跑在8080(可能被后端占了),接口请求的跨域问题需要配置vue.config.js里的代理:

module.exports = { devServer: { port: 8888, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }

这样前端的/api开头的请求全部转发到后端8080端口,解决了跨域问题,前后端联调顺畅。这个代理配置在本地方便,但如果最后要打包成生产部署,就用Nginx做反向代理,把前端和后端挂在同一个域名的不同路径下。我把这个细节写在部署章节的最后,因为很多毕设演示就是在本地跑,记住开发代理这一条就够了。

7. 答辩呈现、接口文档与常见追问拆解

代码跑通只是第一步,把项目的价值讲清楚、把导师可能追问的点提前准备到,才是毕设拿高分的关键。这一章我说些实在的答辩经验。

7.1 接口文档的正确打开方式

配套提供的接口文档是项目的"说明书",但我不建议答辩时灵机一动翻文档,那样显得对项目不熟。正确用法是:把接口文档里每个接口的作用、参数、返回结果都过一遍,做到心里有数。比如导师随口问"供应商分页查询支持哪些条件",你张口就来:支持名称模糊查询、状态筛选、品类筛选、分页,然后顺手在演示页面里操作一遍给他看。

如果接口文档是Swagger自动生成的,答辩时打开Swagger UI界面,也是一种很专业的展示方式。不会手写接口文档的话,项目里可以用springfox-swagger2或者knife4j(国产增强版,页面更好看)生成在线接口文档。Knife4j接入很简单,加依赖、加@EnableKnife4j注解,启动后访问/doc.html就能看到漂亮的接口列表。这个属于一眼就能看到的加分项。

7.2 演示流程怎么走最稳

我的习惯是设计一条"业务故事线"来演示,而不是东点一下西点一下。

推荐顺序:

  1. 登录:演示管理员登录,顺带说一句登录用了JWT Token认证,用户密码是MD5加盐存储。
  2. 仪表盘:一进来先看数据仪表盘,指出供应商总数、品类分布、订单趋势,让导师第一眼觉得"这系统有数据支撑"。
  3. 供应商列表:演示分页、搜索、筛选,点击一个供应商进详情页。
  4. 供应商审核:找一个状态为"待审核"的供应商,演示审核通过,状态从灰色变成绿色标签。
  5. 供应商资质查看:展开详情里边的资质证照列表,指出有有效期预警。
  6. 创建采购订单:从供应商列表切入,选择该供应商的产品,填数量价格生成订单,再到订单列表看到新订单出现。
  7. 供应商评价:给一个合作过的供应商打分,保存后列表出现评价记录。
  8. 图表收尾:回到仪表盘,此时采购订单趋势图多了一个点,说明刚才的演示产生了真实数据变化。

这套演示流程的逻辑是:每个操作步骤都在验证前一个步骤的结果,导师会觉得整个系统浑然一体,而不是各个页面孤立的拼盘。而且最后图表多了一个点这种细节,能展现出系统的数据联动能力,这个点千万别忽略。

7.3 导师必问的问题与回答思路

根据我的经验,导师问来问去就这么几个方向,提前准备就稳了:

  • "你的系统有哪些角色?权限怎么控制的?"答:分管理员和采购员两类;后端用拦截器校验Token,前端通过动态路由控制菜单显隐。如果只做了单一角色,就如实说"当前版本以管理员为核心,后续可以扩展多角色"。
  • "MySQL有几种表?为什么这么设计?"答:说清楚核心表有哪些、哪几张是关联表(中间表)、为什么要拆订单主表和明细表。这是体现你数据库基本功的时刻。
  • "状态字段是怎么设计的?为什么要用数字不用字符串?"答:数字存储省空间、查询快、不容易写错;代码中用枚举去映射,可以避免魔法数字到处飞。同时提一句状态机流转,显得有设计感。
  • "如果供应商数量很大,比如百万级,你的查询怎么优化?"答:这是经典的性能引申问题。你可以先说当前用了单表索引和分页,大规模场景下可以加Redis缓存热点数据、引入Elasticsearch做全文检索、根据查询模式建联合索引、垂直拆分和水平分表。重点是让导师看到你有意识地站在扩展性上思考过。
  • "你做了哪些测试?系统质量怎么保证?"答:功能测试覆盖了所有接口的正常和异常场景,重点测了订单状态流转和权限拦截;如果写了接口测试类也可以提。注意不要吹"做了完整自动化测试",导师随便追问细节就容易露馅。

这些问题都不难,但需要你提前把答案组织好、说到点子上。写论文的时候把这些问题的答案作为核心论据分散到各个章节,论文的查重率和逻辑性都会更好。

7.4 拿着这套项目还能往哪里扩展

毕设交付不是终点。如果后续你想继续完善这个项目,或者面试时拿来当作品集聊,我可以给几个扩展方向,按投入产出比排序:

  • MinIO对象存储替换本地文件存储:让文件模块更贴近生产环境。
  • 引入Redis做验证码缓存和接口限流:登录验证码、供应商列表的缓存,实现起来都不难,面试能聊的深度立刻提升。
  • 基于WebSocket做审核消息通知:供应商提交资料后,管理员页面实时弹出一条待办通知,技术点清晰、演示效果惊艳。
  • 对接EasyExcel做供应商资料批量导入导出:企业中几乎必备,涉及大数据量处理,简历上也能写一条。

这些扩展方向不需要全做,挑一两个就够。它们背后的共同逻辑是:把一个简单的管理页面做到有真实工程的样子,这在面试官和导师眼里含金量是完全不同的。

最后说点实际的个人体会。做毕设这件事,目录上抄十张表不如自己上线跑通一个完整流程。我见过太多同学把时间耗在纠结"选什么技术栈更酷"、"要不要加个算法"上面,最后连基本的增删改查都写得磕磕绊绊。这套供应商管理系统最值得学习的地方,恰恰在于它把一个常见的业务场景用最稳妥的技术栈老老实实落地了——数据建模清楚、接口规范统一、前后端分工明确、能部署能演示,毕设该有的它全有了。你照着跑通一遍、把每一行代码都看明白,再去谈扩展和优化,自然就知道下一步该往哪里走。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询