☰
SpringBoot+Vue高校固定资产管理系统设计与实现详解
2026/10/14 7:53:01 网站建设 项目流程

SpringBoot + Vue 做高校固定资产管理系统的毕设,这套选型在 Java Web 方向里确实是主流中的主流。它不是那种随便拼接出来的 CRUD 四不像,而是把 SpringBoot 的后端工程化、Vue 的前端组件化、MySQL 的数据建模、接口文档的规范,整条链路都打通了。这篇就把整个系统的拆解思路、数据库设计、后端关键实现、前端页面逻辑、还有部署和答辩时能讲的点,一次性说透。

1. 项目整体设计与方案拆解

1.1 为什么固定资产管理是合适的毕设选题

先聊聊选题。很多同学毕设选题要么太玩具,比如图书借阅管理系统,要么太虚,比如什么智能推荐引擎,做出来不像一个能落地的业务系统。固定资产管理属于典型的信息管理系统,它有一个真实的业务闭环,不是只写一张表的增删改查。

高校资产管理的场景特别清晰:学校有大量设备,从教学楼投影到实验室的仪器,从行政办公电脑到教学用的服务器,存在资产录入、领用、归还、调拨、维修、报废、盘点这一整条生命周期。这些流程天然带有状态流转和权限区分,管理员能做什么、普通用户能做什么,这都是有业务含义的。

这个选题对毕设来说有几个实际的好处。第一,业务复杂度适中,不需要算法,不需要高并发,核心逻辑清晰可讲;第二,涉及多表关联、事务、权限、报表统计,评阅老师和答辩现场有东西可问;第三,项目结构和企业里真实的后台管理系统非常接近,讲出去也不虚。

1.2 SpringBoot + Vue 前后端分离的选型理由

技术选型这块,为什么这个项目选择了 SpringBoot 加 Vue,而不是传统的 JSP、Servlet,也不是现在特别重的微服务。

SpringBoot 解决的是后端开发的效率问题。它内嵌 Tomcat,不用单独部署容器,自动配置省掉大量 XML 配置,配合 MyBatis Plus 操作数据库,一个后端模块的开发速度比传统 SSM 快很多。这一点放在毕设场景尤其重要,因为你的时间有限,核心应该放在业务逻辑上,而不是配置环境。

Vue 解决的是前端页面开发的体验问题。传统 JSP 页面是后端渲染,前后端代码混在一起,改个样式都要重启,很痛苦。Vue 的组件化思想把页面拆成一个个组件,资产管理页面、表单弹窗、统计图表,各自独立,数据通过接口请求获取,页面刷新局部更新,不用整页跳转。

前后端分离的核心价值在于,后端只需要提供 RESTful API,前端通过 Axios 发起请求拿到 JSON 数据再渲染。分工清晰,调试也方便,后端用 Postman,前端用浏览器,各自测各自的。

1.3 功能模块与角色权限的全局划分

整个系统按角色可以拆成三类:系统管理员、部门资产管理员、普通教职工。

系统管理员负责全局的资产分类维护、部门管理、用户账户管理、系统总览统计,以及所有资产操作的审批和审计。

部门资产管理员负责本部门的资产入库登记、日常使用的领用审批、调拨申请、维修上报跟进,还有部门内的资产台账管理。

普通教职工能查资产库、提交领用申请、报修、归还,全程只能操作自己的申请单,看不到其他人的流转记录。

从功能模块上看,系统拆成几大块:

  • 首页工作台:展示资产总数、部门资产统计、本月新增、待处理申请等卡片数据
  • 资产管理:资产台账列表、资产入库登记、领用、归还、调拨、维修、报废全流程
  • 资产分类:多级分类维护,比如教学设备、办公设备、图书资料、后勤设施
  • 部门管理:院系与行政部门的组织结构维护
  • 用户管理:账号的增删改查、角色分配、密码重置
  • 统计报表:按分类、按部门、按状态的图表统计与列表导出
  • 记录查询:领用记录、维修记录、调拨记录、操作日志的追溯查询

这个划分兼顾了业务逻辑的完整性和开发的可行性,前端的每个菜单都能对应一套独立的 API 接口,不会出现功能边界模糊、代码到处穿插的问题。

2. 数据库设计:SQL 脚本背后的核心表结构

2.1 核心业务表的划分与关系梳理

这套系统的数据库设计是整个项目的基石。评阅老师最常问的第一个问题就是“你的数据库表是怎么设计的”,如果表之间的关系讲不清楚,后面很难圆回来。

我建议按业务域把表拆成这样几组:

基础信息组:

  • sys_user用户表:user_id、username、password、real_name、dept_id、role_id、status、create_time
  • sys_role角色表:role_id、role_name、role_key、remark
  • sys_dept部门表:dept_id、dept_name、parent_id、leader、phone
  • asset_category资产分类表:category_id、category_name、parent_id、code

资产业务组:

  • asset_info资产信息表:asset_id、asset_code、asset_name、category_id、dept_id、specification、price、purchase_date、warranty_date、status、responsible、location、remark
  • asset_borrow领用归还表:borrow_id、asset_id、user_id、dept_id、borrow_time、return_time、status、remark
  • asset_allocate调拨表:allocate_id、asset_id、from_dept_id、to_dept_id、allocate_time、operator_id、status
  • asset_repair维修表:repair_id、asset_id、report_user_id、repair_reason、repair_time、finish_time、cost、status
  • asset_scrap报废表:scrap_id、asset_id、apply_user_id、scrap_reason、scrap_time、approval_status

辅助功能组:

  • asset_stock_record入库记录表:stock_id、asset_id、dept_id、source、stock_time、operator_id、quantity
  • sys_log操作日志表:log_id、user_id、method、params、create_time
  • sys_menu、sys_role_menu菜单权限表,如果做动态路由和按钮级权限,这两张表是必备的。

表与表之间的关联主要集中在asset_info上。资产信息表的dept_id关联部门表表示当前存放部门,category_id关联分类表表示资产类型,user_id关联用户表表示当前领用人。领用、调拨、维修、报废表都是围绕资产 ID 做流水记录。

2.2 资产编码、状态字段与逻辑删除的细节设计

资产编码的设计很多人会忽略,但它恰恰是最容易出彩的地方。可以采用三段式编码:分类编码 + 年份 + 四位流水号。比如SB-2024-0012,SB 是实验设备分类的编码,2024 是入库年份,0012 是当年该分类下的第 12 条资产。

这种编码规则的好处是,看到编码就知道资产是什么类别,什么时候进来的。数据库层面给asset_code建唯一索引,避免重复录入。代码层生成编码的逻辑是:先根据分类编码和年份查出当前最大流水号,加一后补零。

资产状态字段建议用 int 类型存,0 表示在库,1 表示已领用,2 表示维修中,3 表示已调拨,4 表示已报废。用 int 的好处是查询效率高、排序方便,前端再通过数据字典翻译成中文。对外展示不要直接输出 0、1、2,前端用 statusMap 映射成“在库”“已领用”“维修中”等文本。

逻辑删除一定也要做。资产、部门、分类这些核心表不要物理删除,因为资产一旦删除,历史领用记录、维修记录就全断链了。在表里加一个deleted字段,用 0 和 1 标记。MyBatis Plus 里配好@TableLogic注解,查询的时候自动带上过滤条件,删除操作自动转成 update。

2.3 索引设计与字段类型的实际操作建议

索引是固定资产管理系统里很现实的问题。资产表可能有一两万条数据,不做索引,资产编码模糊查询、部门统计、状态筛选统统会变慢。

一般加索引的规则是:查询条件里频繁出现的字段建普通索引。asset_info表给asset_code建唯一索引,给dept_id、category_id、status分别建普通索引。流水表asset_borrow给asset_id、user_id建索引,asset_repair给asset_id、status建索引。

字段类型方面,有几个易踩的坑。价格字段不要用 float 和 double,精度会出问题,用decimal(10,2)。时间字段统一用datetime,别混用timestamp和date,前端的日期控件直接传字符串,数据库负责存储。金额和流水号相关的字段,SQL 脚本里要写清楚默认值和备注,你自己后面看,也要让老师能看懂。

导入 SQL 脚本的时候千万注意字符集,文件开头加上SET NAMES utf8mb4;,数据库建库也要指定utf8mb4,否则中文乱码问题会在你演示的时候突然冒出来,非常尴尬。

3. 后端核心实现:SpringBoot 项目的关键代码逻辑

3.1 项目分层与统一返回格式

后端工程的分层尽量标准:controller、service、mapper、entity、dto、vo、config、common。

controller只做参数接收和返回;service写业务逻辑;mapper负责数据库操作接口;entity对应数据库表字段;dto用于接收前端参数,vo用于返回给前端的数据结构。为什么不直接返回 entity?因为前端要的数据格式和后端表结构不一定一致,领用记录需要连表查出用户姓名和部门名称,直接在 entity 上塞多余字段很乱,用 vo 封装一次,干净又安全。

统一返回结果集是必备的。后端所有接口都返回一个Result<T>对象,包含 code、message、data 三个字段。成功返回 200,业务异常返回 500,未登录返回 401,无权限返回 403。前端 Axios 响应拦截器只需要判断 code 就能统一做提示,不用每个接口单独写错误处理。

public class Result<T> { private Integer code; private String message; private T data; public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("操作成功"); result.setData(data); return result; } public static <T> Result<T> error(String message) { Result<T> result = new Result<>(); result.setCode(500); result.setMessage(message); return result; } }

配合@RestControllerAdvice做全局异常处理。业务异常用自定义的BusinessException抛出,参数异常用@Valid注解校验后统一拦截。这样 controller 层代码非常干净,大部分都不需要 try-catch,这是一个很重要的工程习惯。

3.2 JWT 登录认证与权限控制的实现思路

登录认证用的是 JWT。用户输入用户名密码,后端校验通过后签发一个 token,token 里包含 userId 和 username,然后前端每次请求在 header 里带上 Authorization。后端用一个拦截器或者 Spring Security 过滤器来校验 token。

如果不想引入 Spring Security 那么重的东西,可以直接用拦截器实现,自己写一个JwtInterceptor,实现HandlerInterceptor接口,在preHandle里从请求头取 token,解析出来把 userId 和 username 放到 ThreadLocal,然后放行。同时指定拦截路径,登录接口、校验验证码接口不需要拦截,其他接口全部拦截。

权限控制这块,建议做一个简单的角色判断。后端在需要权限的接口上,用自定义注解@RequirePermission("asset:add")标记,拦截器里解析当前用户的角色,再判断是否有对应权限码。这样不用引入完整 RBAC 框架,也能把前端菜单权限和后端接口权限串起来。

密码存的是 BCrypt 加密后的密文,数据库里不要出现明文密码。初始化的时候在 SQL 脚本里放入一个预先加密好的管理员密码,或者干脆写一段 CommandLineRunner 在项目启动时检查并初始化管理员账户,这样你重新导入数据库后不用手动去改密码。

3.3 资产领用、归还、调拨、维修的业务状态流转

资产全流程是最能体现业务逻辑的部分,也是答辩时最容易说清楚的部分。简单梳理一下每个流程的状态机。

领用流程:用户提交领用申请,资产状态从在库变成待领用;管理员审批通过后,状态变成已领用,同时更新asset_info的责任人和存放部门,往asset_borrow插一条记录;归还时,状态从不占用变回在库,更新归还时间。

这个流程在代码里要注意事务。领用审批时,既要在asset_borrow表插入记录,又要更新asset_info的资产状态,这两步必须放在同一个事务里,否则会出现领用记录已经生成但资产状态没变的情况。

调拨流程:调拨的本质是资产的部门归属变更。从 A 部门调到 B 部门,提交调拨申请时记录 from_dept_id 和 to_dept_id,审批通过后回写资产表。前端展示时需要显示原部门和新部门,所以调拨记录表不能只保存调拨后的部门。

维修流程:资产报修后,状态切到维修中,维修表记录报修原因和费用;维修完成,状态恢复到之前的可用状态。这里需要单独设计一个状态位,比如repairStatus来区分修复和报废,因为如果维修费大于资产原值,业务上会选择直接报废。

这个状态流转的逻辑,建议在每个状态变更接口里先做前置校验,比如资产已报废就不能再领用,已领用的资产不能重复领用。校验逻辑写在 service 层最前面,不符合条件就直接抛异常,不要等脏数据落库后再查。

3.4 分页查询、条件筛选与 Excel 导入导出

列表页面的分页查询使用 MyBatis Plus 的Page对象。控制器接收当前页和每页条数,service 层用LambdaQueryWrapper构造条件,比如资产名称的模糊查询、状态精确匹配、部门和时间范围筛选,最后返回IPage对象。

Page<AssetVo> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<AssetInfo> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(name), AssetInfo::getAssetName, name) .eq(status != null, AssetInfo::getStatus, status) .eq(deptId != null, AssetInfo::getDeptId, deptId) .between(startDate != null && endDate != null, AssetInfo::getPurchaseDate, startDate, endDate) .orderByDesc(AssetInfo::getCreateTime);

注意一个坑,分页插件不生效的常见原因是没配置分页拦截器。只有在 MyBatis Plus 配置类里添加PaginationInnerInterceptor,分页查询才会自动拼接 limit 语句,否则查出来的是全量数据,前端翻页永远是第一页。

Excel 导入导出用的 EasyExcel。导出就是查询列表数据,写入 Excel 后响应给前端下载;导入就是接收 MultipartFile,一行行解析后校验字段,再插入数据库。导入的坑在于,Excel 里用户填的部门名称和数据库里的不一定一致,建议先根据部门名称批量查出部门 ID,做一次映射,再入库。导出的坑在于文件名编码,用URLEncoder.encode(fileName, "UTF-8"),否则浏览器下载时中文文件名会乱码。

4. 前端实现:Vue 项目从页面到接口的完整链路

4.1 前端工程结构与路由拆分

前端工程用 Vue CLI 或 Vite 初始化,结构上分成api、router、store、views、components、utils几个目录。api目录下按模块拆分接口,比如asset.js、user.js、dashboard.js,每个文件导出对应模块的请求函数。views目录下按页面拆分,资产台账页面、领用管理、维修管理、统计报表,一个业务模块一个文件夹。

路由拆分的建议是:登录页独立,主布局里挂载各个业务页面。同时要在路由beforeEach里做登录态判断,没有 token 一律跳到登录页。如果一个页面需要管理员权限,可以在路由 meta 里配置 roles,然后做跳转拦截。

如果做按钮级权限,前端拿到后端返回的权限码列表后,存到 store 里,页面上用自定义指令v-permission="'asset:add'"判断当前用户是否有权显示按钮。没有权限码的按钮直接不渲染,这样至少从界面上规避了普通用户看到管理员按钮的尴尬。

4.2 资产管理页面的表格、查询表单与弹窗交互

资产管理页面的核心是资产台账列表。布局上,上半部分是一个查询表单,包含资产名称、资产状态、所属部门、入库日期范围四个查询条件;下半部分是一张表格,展示资产编码、资产名称、分类、部门、价格、状态、购置日期和操作按钮。

表格用 el-table 实现,核心字段用el-table-column渲染。状态列不要直接展示 0 和 1,用formatter函数映射成 tag 标签,比如在库是绿色标签,维修中是橙色标签,报废是灰色标签。操作列根据状态显示不同按钮,在库资产显示领用和调拨,已领用显示归还,维修中显示维修完成,这个细节会让页面交互感强很多。

新增和编辑资产建议用弹窗里的 el-form 实现,表单校验规则用 rules 配置。注意分类选择要用级联选择器,因为分类表是多级的;部门选择也要配成树形结构,否则几十个部门放在下拉框里不好选。

前端的一个小经验是,表格数据请求和搜索条件联动时,直接用一个对象作为参数传给后端。每次搜索条件变化时页码重置为 1,然后重新请求数据。分页组件el-pagination的 current-page 和 page-size 要绑定固定变量,否则点击翻页后查询条件丢失。

4.3 Axios 封装与数据可视化统计

Axios 封装在utils/request.js里。请求拦截器从 localStorage 或 store 里取 token,塞进请求头;响应拦截器统一处理返回的 code。code 是 200 就直接返回 data,401 表示 token 过期,跳转登录页并清除本地 token,其他错误码统一弹 ElMessage 提示,后端每个接口都不用写错误处理逻辑。

service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = token } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code === 200) { return res.data } if (res.code === 401) { localStorage.removeItem('token') router.push('/login') } ElMessage.error(res.message) return Promise.reject(new Error(res.message)) }, error => { ElMessage.error('网络请求异常') return Promise.reject(error) } )

统计报表页面用 ECharts 做可视化。常见的有三个图表:按资产分类的饼图、按部门资产数量的柱状图、按月份新增资产的折线图。数据来源是后端统计接口,返回 id 和 value 的列表,前端直接转换成 ECharts 的 data 格式。

这里有个后端优化的点,统计接口可以一次性返回多个图表需要的数据,封装成一个 DashboardVO,前端一次请求全部拿到,而不是发四个接口。系统首页加载卡顿的问题,多半是统计接口查得太慢,统计时要保证 group by 的字段有索引,日期按月份统计可以用DATE_FORMAT(purchase_date, '%Y-%m')来做分组。

5. 接口文档与项目部署: 上线前必须做好的两件事

5.1 Swagger 接口文档的配置与用法

接口文档是这套项目源码里很重要的一环,也是很多人容易忽略掉的内容。用 Swagger 生成文档,在 SpringBoot 里加依赖,然后用注解描述接口信息。

后端每个 controller 加@Api(tags = "资产管理接口"),每个接口方法加@ApiOperation("资产列表查询"),参数对象用@ApiModelProperty("资产名称")描述字段。配置完成后访问http://localhost:8080/swagger-ui/index.html就能看到全部接口。

接口文档的价值不只是给答辩老师看,你自己调试前端的时候也很重要。前端对接接口时有歧义,直接查文档确认字段名和类型,不用去翻后端代码。好的文档习惯是:字段命名统一用驼峰,时间参数格式统一是yyyy-MM-dd HH:mm:ss,分页参数固定用 pageNum 和 pageSize,这样前后端联调效率会高很多。

5.2 本地启动:数据库导入、后端配置、前端环境

本地把项目跑起来的完整过程值得写一遍,因为很多同学第一次拿到源码,卡在环境配置上很久。

第一步,准备好 JDK、Maven、Node.js 和 MySQL。建议版本组合是 JDK 8 配 SpringBoot 2.x,Node.js 14 以上配 Vue 2 或 Vue 3,MySQL 5.7 或 8.0 都可以。

第二步,导入数据库。直接用 Navicat 执行项目里的 SQL 脚本,注意先建库再导入,数据库名称和脚本里保持一致,比如asset_management。

第三步,修改后端配置文件application.yml。数据库地址、用户名、密码必须改成自己本地的,另外检查一下 redis 配置,如果没有安装 Redis,就要把验证码存储相关的配置改成本地 Map 形式,或者干脆先跳过验证码功能。

第四步,启动后端。在项目根目录执行mvn spring-boot:run,看到控制台打印启动成功就说明没问题。如果端口被占用,修改配置里的 server.port,或者看看是不是上一次启动的进程没关掉。

第五步,启动前端。在frontend目录执行npm install,安装依赖后执行npm run dev。前端开发服务器的默认端口是 8080,如果和后端冲突就改一下vue.config.js里的 port,同时配好 devServer 的 proxy 把/api转发到后端端口。

5.3 前后端联调与打包部署方案

本地联调时,一个常见问题是跨域。最省事的处理是在前端vue.config.js里配置代理,开发环境请求统一走/api前缀,代理转发到后端服务,这样浏览器看到的都是同源请求,不需要后端加 CORS 配置。

devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } }

如果后端也配了 CORS 允许跨域,双端会同时存在跨域配置,开发环境反而可能出现重复问题。建议只保留一端的跨域处理,后端留一个全局 CORS 配置,前端不要代理,或者前端用代理,后端不做跨域,二选一即可。

打包部署如果是用来答辩演示,在本地跑开发环境完全够用。如果只是临时部署到服务器演示,提供一个 Nginx 部署页面:前端打包成 dist 目录,放到 Nginx 的 html 目录,反向代理/api到后端端口,后端maven package打成 jar 包,nohup java -jar启动。这个方案简单可靠,答辩前能快速搭出环境。

6. 常见问题与排查技巧实录

6.1 数据库连接失败、时区与编码问题

后端启动时最常见的报错是数据库连接失败,要么是数据库没启动,要么是密码错了,要么是时区和编码问题。连接串的末尾一定要加上serverTimezone=Asia/Shanghai和characterEncoding=utf8,MySQL 8.x 的驱动用com.mysql.cj.jdbc.Driver,新版驱动类名和旧版不一样,配置错会直接报类找不到。

中文乱码问题排查顺序:先看数据库表字符集是不是 utf8mb4,再看后端连接串是不是指定了编码,最后看前端页面 meta 标签。一条条检查,基本都能定位出来。

6.2 启动报错:端口占用与依赖版本冲突

端口占用是最常见的启动失败原因,报错信息里会有Port already in use。Windows 下用netstat -ano | findstr 8080查进程号,然后taskkill /F /PID 进程号杀掉。Linux 下用lsof -i:8080查端口。

依赖版本冲突主要出现在 SpringBoot、MyBatis Plus、MyBatis 之间的版本组合。SpringBoot 2.7.x 对 MyBatis Plus 的兼容性比较好,如果用的是 3.5.x 版本,找对应的 mybatis-plus-boot-starter。遇到启动报错找不到 SqlSessionFactory 之类的,基本是版本不匹配,直接统一降级或升级到官方文档推荐的组合。

6.3 前端接口调不通与菜单权限返回 401 问题

前端调后端接口调不通,先把网络请求面板打开,看 404 还是 401 还是 500。

404 说明后端没有这个接口路径,检查前端请求的 URL 和后端 controller 的映射是否一致,特别是@RequestMapping里的类级别路径,漏掉前缀是最常见的。401 说明 token 没有带或者 token 过期,检查请求拦截器是不是取错了 token 字段。500 说明后端代码运行时出了异常,看后端控制台的堆栈日志,大部分是空指针或者 SQL 语句报错。

菜单权限返回 401 还有一个常见原因是,后端拦截器放行了登录接口,但没有放行前端首次加载时请求用户信息的接口。排查的时候把未登录时需要访问的接口全部加到白名单里。

6.4 我的踩坑心得: 拿到一套源码后应该怎么快速上手

如果你拿到的是别人整理好的完整项目源码,别急着直接启动。先花十五分钟把三个东西看清楚:数据库脚本里的表结构、后端 controller 的接口列表、前端路由对应的页面。这三样看完,整个系统的脉络就大概清楚了。

然后启动起来之后,不要只看页面好不好看,重点走一遍核心流程:新建资产、领用这个资产、再归还这个资产。每一步在数据库里对应哪条 update,哪条 insert,心里要有数。答辩的时候说得出这一套流程,比背十个概念点都要有说服力。

遇到暴错不要慌,先看后端控制台日志,再看前端网络请求的响应体。大部分毕设项目的问题都不是逻辑难题,而是环境、路径、字段名对不上,一层层拆开排查就好了。

我个人在实际操作中的体会是,固定资产管理系统这种选题,核心不在于代码写得多么花哨,而在于业务逻辑是否完整、数据流转是否闭环。把资产生命周期这条主线讲明白,每一张表为什么存在,每一个字段为什么需要,每一个状态为什么这样流转,你能把这三个问题回答得清楚流畅,再加上一套完整的前后端代码,这个毕设的质量就是稳的。

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

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

立即咨询