ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue+MyBatis+MySQL资产管理系统开发与部署全攻略

SpringBoot+Vue+MyBatis+MySQL资产管理系统开发与部署全攻略 公司里资产登记一直是个让人头疼的活之前靠Excel管电脑、显示器、办公椅这些资产散落在各个部门领用记录靠纸质单子盘点的时候翻半天也查不清一件设备到底在谁手里、什么时候借走的。后来我花了两周时间用SpringBootVueMyBatisMySQL这套前后端分离的架构从零搭了一套公司资产网站系统包含完整的资产登记、领用归还、分类查询、统计看板功能并且把部署流程也完整跑通了。这篇文章就把整个项目的设计思路、核心代码的写法、以及从本地到服务器的部署过程全部拆开讲一遍适合正在做前后端分离项目实战、准备毕业设计或者公司内部需要快速搭一套轻量资产管理系统的同学参考。1. 这套资产系统到底做了什么以及为什么这么搭1.1 资产管理的真实痛点与功能边界我在动手之前先跟行政和财务的同事聊了一圈整理出几个最核心的诉求。首先是资产台账所有固定资产要有统一的明细记录包括资产编号、名称、分类、购买日期、价格、当前状态不能再靠多份Excel来回传。其次是领用与归还员工领走一台笔记本或者显示器系统里必须能查到领用人、领用时间、预计归还时间归还的时候也要有记录这样出了问题能追溯到人。第三是分类统计比如按部门看资产分布按资产类别看数量和价值方便年底盘点。所以这套系统最后圈定的功能边界很明确登录认证、资产管理增删改查、资产分类管理、领用管理领用、归还、历史记录、部门管理、用户管理、资产统计仪表盘。没做审批流没做资产折旧计算也没对接企业微信和钉钉先把最核心的资产生命周期管起来。这里要给个建议做内部系统功能边界宁可收窄一点先把主流程跑顺后面再加不迟。很多项目烂尾就是因为一开始想要的太多。1.2 选型SpringBootVueMyBatisMySQL的理由这套技术栈放到今天来看不算新潮但绝对是最稳妥的组合之一。SpringBoot负责后端接口自带内嵌Tomcat打包成jar就能跑省去独立配置Tomcat的麻烦Vue负责前端页面用Vue CLI创建工程组件化开发配合Element UI这种现成的组件库几天就能把管理后台的界面搭出来MyBatis作为持久层框架SQL由自己控制业务逻辑复杂的时候写动态SQL非常灵活比全自动的JPA更容易排查问题MySQL存数据公司内部系统几百号人的访问量性能绰绰有余。为什么不用更重的微服务架构因为没必要。这系统的复杂度就是单应用级别拆成微服务徒增部署和运维成本。为什么不用若依框架这类现成的后台脚手架若依确实把权限、代码生成都给你备好了但如果你要练手或者源码要交作业从零搭一遍你对每个环节的掌握程度完全不一样。我个人建议学习阶段自己搭一遍生产环境图省事再考虑现成框架。2. 数据库设计与后端核心链路2.1 资产相关表结构的设计逻辑数据库我设计了6张核心表分别是用户表sys_user、部门表sys_dept、资产分类表asset_category、资产表asset、领用记录表asset_borrow_record、操作日志表sys_log。这里把最核心的资产表和领用记录表的结构拿出来说。资产表asset的核心字段是asset_id主键、asset_no资产编号唯一、asset_name资产名称、category_id分类外键、dept_id所属部门外键、status状态0在库、1已领用、2维修中、3已报废、price购买价格、buy_date购买日期、remark备注。create_time和update_time用MyBatis的自动填充功能插入和更新的时候自动写入当前时间。领用记录表asset_borrow_record的核心字段record_id主键、asset_id资产外键、user_id领用人外键、borrow_time领用时间、expected_return_time预计归还时间、actual_return_time实际归还时间、status状态0借用中、1已归还、remark备注。这里有几个设计细节值得注意。第一资产编号必须唯一我采用规则是“类别前缀年月日三位流水号”比如PC20250115001这样看编号就能大致判断资产类别和入库时间实际使用中非常方便。第二资产状态和领用状态要区分开资产的status代表物理状态领用记录的status代表借用流程状态一个资产当前是否被借出通过查它的status字段和未归还的领用记录来确认。第三删除策略上资产和分类都做逻辑删除用一个deleted字段标记查询的时候统一过滤掉防止历史数据关联断裂。2.2 MyBatis在分页、多表联查、动态SQL上的实际写法MyBatis在这个项目里承担了所有数据访问操作我重点用到了三个能力动态SQL、多表联查和分页插件PageHelper。资产列表页的查询条件比较多资产名称模糊搜索、分类筛选、状态筛选、部门筛选条件组合是不确定的这时MyBatis的动态SQL就派上用场了。用 标签加 判断条件的拼接交给MyBatis处理不会出现多一个AND语法错误。这是我写的Mapper中的一个典型片段select idselectAssetList resultTypecom.example.asset.entity.AssetVO SELECT a.asset_id, a.asset_no, a.asset_name, a.price, a.buy_date, a.status, c.category_name, d.dept_name FROM asset a LEFT JOIN asset_category c ON a.category_id c.category_id LEFT JOIN sys_dept d ON a.dept_id d.dept_id WHERE a.deleted 0 if testassetName ! null and assetName ! AND a.asset_name LIKE CONCAT(%, #{assetName}, %) /if if testcategoryId ! null AND a.category_id #{categoryId} /if if teststatus ! null AND a.status #{status} /if if testdeptId ! null AND a.dept_id #{deptId} /if ORDER BY a.create_time DESC /select多表联查这块资产列表需要显示分类名称和部门名称而不是只显示ID所以用LEFT JOIN把assetc_category和sys_dept带出来。分页的话用PageHelper插件在Service层调用PageHelper.startPage(pageNum, pageSize)紧接着执行查询返回的就是分页后的结果然后用PageInfo包装一下返回总条数和总页数。需要注意PageHelper的生效原理是拦截下一条执行的SQL所以startPage方法必须在查询语句执行之前调用中间不能再有其他SQL操作这是新手最容易踩的坑。另外说下MyBatis二级缓存。排行榜上经常能看到“mybatis二级缓存实现”这种热搜词我在这套系统里没有开启二级缓存因为资产数据的实时性要求比较高而且同一时刻并发修改并不多。如果你要做记住二级缓存是namespace级别的多个Mapper操作同一张表的时候容易读到脏数据配置之前先想清楚你的数据一致性要求。2.3 Controller-Service-Mapper的执行链路与统一返回体后端接口我遵循标准的Controller-Service-Mapper三层结构。Controller层只负责接收参数、校验参数、调用Service、返回结果不写业务逻辑Service层处理所有业务规则比如资产领用时先检查资产状态是否为在库、再更新资产状态、同时插入领用记录这整个过程必须加事务注解TransactionalMapper层只做数据访问。为了前后端接口规范统一我定义了一个Result 返回体public class ResultT { private Integer code; // 200成功 500失败 private String msg; // 提示信息 private T data; // 返回数据 }所有Controller的返回值都是Result类型前端axios拦截器统一处理code非200直接弹出错误提示。Token认证用的是JWT登录成功后后端生成token返回前端存在localStorage里每次请求在axios拦截器中塞进Authorization请求头后端用一个拦截器Interceptor校验token放行登录接口和静态资源路径。这套方案在前后端分离项目里相当通用比Session方案更适合接口部署在独立域名的场景。3. 前端Vue的实现与前后端联调3.1 页面模块划分与路由设计前端用Vue 2 Element UI Vue Router Vuex Axios这套组合用Vue CLI创建工程。页面结构按管理后台的常见布局来左侧菜单、顶部栏、中间内容区。路由分两部分一部分是登录页不需要登录就能访问另一部分在Layout布局组件内部是业务页面通过路由守卫统一校验登录状态没有token直接重定向到登录页。具体页面有登录页、仪表盘、资产管理页、资产新增/编辑页用弹窗实现、资产分类管理页、领用管理页、领用历史页、部门管理页、用户管理页。路由配置里注意子路由的写法所有业务页面都挂在Layout下面用children数组配置对应的component路径是相对Layout的路径。给一个典型的路由代码示例{ path: /layout, component: Layout, redirect: /dashboard, children: [ { path: /dashboard, name: Dashboard, component: () import(/views/Dashboard.vue) }, { path: /asset/list, name: AssetList, component: () import(/views/asset/AssetList.vue) }, { path: /asset/borrow, name: BorrowManage, component: () import(/views/asset/BorrowManage.vue) } ] }3.2 API请求封装与令牌处理我在src目录下建了一个api文件夹里面按模块拆文件auth.js、asset.js、category.js、borrow.js、dept.js、user.js。每个文件导出一个对象方法里调用封装好的request函数。这个request函数是核心基于axios实例创建做了三件事设置baseURL、请求拦截器加token、响应拦截器统一处理错误码。// src/utils/request.js import axios from axios import { Message } from element-ui import router from /router const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }, error Promise.reject(error)) service.interceptors.response.use(response { const res response.data if (res.code ! 200) { Message.error(res.msg || 请求失败) if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.msg)) } return res }, error Promise.reject(error)) export default servicebaseURL我放在.env.development和.env.production两个环境变量文件里开发环境指向本地后端地址http://localhost:8080生产环境指向nginx反向代理的地址/api这样同一套代码切换环境只需要改环境变量文件不需要动业务代码。这是Vue项目中环境配置的标准做法热搜里经常看到“vue安装及环境配置”很多新人把baseURL硬编码在业务代码里后面部署的时候到处找着改非常痛苦。3.3 资产列表、新增、领用归还这些关键页面怎么实现资产管理列表页是最核心的页面。顶部是搜索区包括资产名称输入框、分类下拉框、状态下拉框、查询和重置按钮中间是操作按钮区新增资产、批量导出可选、刷新下面是el-table表格列展示资产编号、名称、分类、所属部门、价格、状态、购买日期、操作按钮。状态列我用el-tag做不同颜色的标签在库是绿色已领用是橙色维修中是红色报废是灰色视觉效果清晰。新增资产用el-dialog弹窗内嵌el-form表单实现提交前做表单校验资产编号、资产名称、分类、价格为必填项校验规则用rules配置。提交成功后关闭弹窗并刷新列表。价格字段记得用el-input-number组件避免用户在文本输入框里填乱七八糟的内容。领用管理页的逻辑稍微复杂一点。页面上方展示所有状态为“在库”的资产列表操作按钮是“领用”点击后弹出领用弹窗选择领用人从用户列表下拉选择、填写预计归还时间。后端接口做的事情是检查资产状态、更新资产状态为已领用、插入领用记录三步操作在一个事务里。归还操作在“借用中”的tab下点击归还后端接口把领用记录的actual_return_time设为当前时间、状态改为已归还同时把资产状态改回在库。前后端联调的时候要特别注意字段名的一致性。后端实体类用驼峰命名比如assetName前端表单绑定值和表格prop属性也要写成assetName千万注意JSON序列化的时候字段名别改成下划线风格除非前端也跟着改。我见过很多联调半天对不上的问题都是字段命名不一致导致的开发阶段先在浏览器F12看一下Network面板里的实际请求参数和响应JSON确认无误再做页面。4. 从零开始的完整部署教程4.1 整体部署架构与前置准备这套系统部署时我采用的是标准的前后端分离部署架构前端Vue项目打包成静态文件用Nginx托管后端SpringBoot打包成jar包用java -jar命令运行MySQL单独部署在一台服务器也可以跟后端同一台前端通过Nginx反向代理把/api路径的请求转发到后端的8080端口。这样做的好处是前后端完全解耦前端静态文件由Nginx处理性能更好后端只负责接口将来扩容只需要增加后端实例。前置准备清单如下一台Linux服务器CentOS 7或Ubuntu 20.04都行2核4G起步服务器上安装好JDK 1.8、Maven、Nginx、MySQL 5.7或8.0本地开发好的前后端源码域名可选用IP访问也行这里插一句很多人搜“tomcat部署前后端分离项目”传统做法是把后端打成war包丢进Tomcat的webapps目录前端build后的dist文件夹丢进Tomcat的webapps/ROOT下让Tomcat同时托管前后端。这种方式能用但本质上还是把前端当成静态资源来托管Nginx方案更主流nginx托管静态文件并发能力强配置反向代理也灵活。4.2 后端打包与启动后端打包前先检查application.yml中的配置。生产环境我建议把配置拆开用application-prod.yml单独放生产配置启动时通过--spring.profiles.activeprod指定使用哪个配置。数据库连接配置要改成服务器上的地址比如jdbc:mysql://localhost:3306/asset_db用户名密码改成自己的。然后用Maven打jar包在项目根目录执行mvn clean package -Dmaven.test.skiptrue打完的jar在target目录下文件名类似asset-system-1.0.0.jar。把这个jar上传到服务器想放哪里放哪里我习惯放在/opt/asset-system目录下。启动命令是java -jar /opt/asset-system/asset-system-1.0.0.jar --spring.profiles.activeprod后台运行的话用nohupnohup java -jar /opt/asset-system/asset-system-1.0.0.jar --spring.profiles.activeprod /opt/asset-system/logs/app.log 21 启动成功后可以先在服务器本地验证一下接口curl http://localhost:8080/api/asset/list?pageNum1pageSize10如果返回JSON数据说明后端启动成功。这里提醒一个SpringBoot版本的问题如果你本地用的是SpringBoot 3.x那要求JDK 17很多服务器上默认是JDK 1.8直接跑会报UnsupportedClassVersionError。稳妥做法是服务器装跟本地一致的JDK版本或者干脆在本地开发时就用SpringBoot 2.7.x配JDK 1.8兼容性最好。4.3 前端打包与Nginx配置前端打包前先确认.env.production文件里的VUE_APP_BASE_API值。这里有个关键点生产环境的前端请求地址我建议写成/api而不是完整的http://ip:8080原因是通过Nginx反向代理可以隐藏后端真实端口同时规避跨域问题。打包命令npm run build打包完成后项目根目录会生成dist文件夹里面是index.html和static目录CSS、JS等资源文件。把dist文件夹里的所有文件上传到服务器的Nginx静态目录我习惯放在/usr/share/nginx/html/asset-system/下或者/var/www/asset-system/路径自定义。接下来配置Nginx在/etc/nginx/conf.d/目录下新建一个配置文件asset.conf内容如下server { listen 80; server_name your-domain-or-ip; # 前端静态资源 location / { root /var/www/asset-system; index index.html; try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }try_files这行配置很重要Vue是单页应用前端路由由vue-router控制刷新页面的时候Nginx要去找到对应的路由文件但前端实际上只有一个index.html所以不管路由是/asset/list还是/dashboard都return回index.html由前端js去解析对应页面。配置完检查语法并重载nginx -t nginx -s reload然后浏览器访问服务器IP看能不能打开登录页。能打开登录页说明前端部署成功登录一下走通全流程整个系统就上线了。4.4 数据库初始化与数据导入数据库部分先在服务器MySQL里创建数据库CREATE DATABASE asset_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后导入本地的SQL脚本用命令行方式mysql -u root -p asset_db asset_db.sql如果你本地用的是Navicat导出SQL注意导出时勾选“包含建表语句”和“包含数据”服务器的MySQL版本要跟本地兼容本地5.7导出的SQL导入8.0基本没问题反过来就要检查语法兼容性。还有一个高频坑是MySQL 8.0默认的认证插件是caching_sha2_password而JDBC驱动如果版本较旧会报Unable to load authentication plugin解决办法是换新版本的mysql-connector-java或者在MySQL里把用户的认证方式改成mysql_native_password。本地能用不代表服务器能用数据库字符集和认证方式这两处一定要检查。5. 部署后最容易踩的坑与排查思路5.1 前端白屏与静态资源404部署后第一个常见问题是访问首页白屏按F12看Console报错一般是资源路径不对。Vue CLI默认的publicPath是/如果你的前端部署在域名根路径下没问题但如果部署在子路径比如/asset-system/资源路径就会404。解决办法是修改vue.config.js里的publicPath配置或者用相对路径./。我一般直接部署在根路径省心。第二个常见问题是刷新页面404比如你访问http://ip/dashboardNginx直接返回404。这个问题就是我前面说的Nginx配置里少了try_files这行加上就好了。5.2 跨域问题前端通过/api代理请求后端这种方案不会有跨域问题。如果你没走代理前端直接用http://ip:8080去调用接口浏览器就会报跨域错误。解决办法有两种一是后端加CORS配置写一个WebMvcConfigurer配置类这是后端的解决办法二是前端通过Nginx代理改一下baseURL就行。我推荐第二种思路生产环境用反向代理更规范。开发环境下在Vue的vue.config.js里配置devServer的proxy让前端开发服务器把/api转发到localhost:8080这是开发阶段的标准做法。// vue.config.js devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }5.3 MySQL连接报错的两个经典问题“mysql ssl连接错误”这个热搜词很多人搜。JDBC连接MySQL 8.0的时候如果不加ssl参数可能会报ssl连接相关错误或者只是看到警告信息。我在jdbc配置里加上useSSLfalse关掉SSL验证再加serverTimezoneAsia/Shanghai解决时区问题。另外连接池断开的问题如果MySQL的wait_timeout较短空闲连接会被服务端断开客户端下次使用时报连接错误我在配置里加上autoReconnecttrue或者配置HikariCP连接池的参数比如maximum-pool-size和minimum-idle让连接池自动管理连接。完整的jdbc配置参考spring: datasource: url: jdbc:mysql://localhost:3306/asset_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiautoReconnecttrue username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver5.4 端口占与防火墙后端启动成功但外部访问不了先检查防火墙。CentOS 7默认防火墙可能开着8080端口没放行或者80端口没放行。执行firewall-cmd --zonepublic --add-port8080/tcp --permanent firewall-cmd --zonepublic --add-port80/tcp --permanent firewall-cmd --reload如果是云服务器还要检查安全组规则在云控制台放行对应端口。这个坑非常隐蔽服务器本地curl通外网死活访问不了多半是防火墙或安全组挡住了。还有一个细节如果80端口被nginx占用了直接改nginx配置监听其他端口或者干脆就用默认的80。如果服务器上还跑着其他web服务注意端口冲突启动失败的时候用netstat -tlnp查一下端口占用情况。6. 这套系统后续可以怎么扩展部署上线稳定运行几个月之后我陆续做了一些扩展这里给你几个方向作为参考。第一个是加Excel导入导出功能资产盘点的时候直接在系统里导出资产清单Excel盘点完再导入更新状态用EasyPoi或者EasyExcel可以快速实现。第二个是加操作日志资产每一次新增、修改、领用、归还都记录下来方便追踪责任我在设计初期预留了sys_log表后来只用AOP加一个切面就实现了日志记录。第三个是加资产二维码标签为每个资产生成唯一的二维码打印出来贴在设备上手机扫码直接查看资产信息和领用状态这个用前端qrcode库可以很轻松实现。如果你打算把这套系统作为毕设或面试项目建议重点关注三个点权限控制的完整实现用户-角色-菜单的RBAC模型、资产领用归还的事务一致性演示、以及部署过程中的排障经验。把这些讲清楚面试官对你的评价通常会高很多。最后分享一个我自己的部署习惯每次上线前先在本地用Maven打包并在本地把jar跑起来再在服务器上拉代码重新打包部署这样能第一时间暴露环境差异导致的问题。部署文档一定要写清楚每一步命令和配置别嫌麻烦等过几个月再来看这份文档能帮你快速回忆起整个项目的部署细节。
返回列表