ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue前后端分离公司资产管理系统实战全解析

SpringBoot+Vue前后端分离公司资产管理系统实战全解析 这套“前后端分离公司资产网站系统”是我最近在公司从零到一搭完的一个完整项目技术栈就是标题里那套SpringBoot Vue MyBatis MySQL。前后端分离这个词快被说烂了但真正把一个资产管理系统从数据库设计、后端接口、前端页面一直做到服务器部署中间全是细节。这篇文章没什么炫技内容就是把整个过程拆开讲清楚每一步是怎么选的、怎么写的、怎么踩坑爬出来的。适合谁来参考如果你正在做毕业设计、想进中小团队快速搭一套内部资产目录、或者刚接触前后端分离项目想找一份完整实战链路这篇内容可以直接当成路线图用。代码结构我会按模块说明关键的配置和实现方式也都会贴出来按步骤走基本能复现一套可运行的系统。1. 项目定位与整体设计思路拆解1.1 公司资产盘点为什么需要一套网站系统刚接手这个需求的时候公司资产管理还在用Excel登记每个人领了什么电脑、哪个会议室多了台投影仪全靠行政手工记。小公司几十个人还好一旦过了一两百人Excel就开始失控同名设备分不清、借出去的东西没人记得还、年底盘点时更是兵荒马乱。所以这个项目要解决的其实是一件事让每一件资产的“生命周期”都留下可追溯的记录。从资产录入开始到领用、归还、调拨、报废每一步都落到数据库里管理层随时能看到“现在公司有多少台笔记本在库、多少台被借走、谁手上资产最多”。网站系统只是外壳核心是资产台账 借还记录 统计看板这三个闭环。用户角色也比较清晰普通员工能看到资产列表并发起借用管理员负责录入、编目、审批和处理借还。如果要做扩展还可以加上二维码标签打印、盘点任务拆分、折旧计算这些功能但第一版先把主链路跑通这是最务实的做法。1.2 技术选型为什么是SpringBootVueMyBatisMySQL这套组合在Java后端开发里已经是“经济适用型”的标准答案了。SpringBoot负责快速搭起RESTful接口MyBatis管SQL和ORM映射MySQL存数据Vue做前端页面。选这个组合不是因为它是最新的而是因为它生态最成熟、招人最容易、出问题最好查。周边资料满天飞遇到不会的搜一下基本都有答案。对比一下传统单体方案以前做这种系统喜欢用JSP Spring MVC页面和后端代码混在一起改个样式都要重启项目。前后端分离之后后端只输出JSON前端拿数据自己渲染页面两边通过接口协议解耦。开发期可以并行推进部署期前端丢到Nginx、后端跑Java进程互不干扰这才是真正能提高开发效率的做法。也有朋友问我为什么不直接用若依这类开源前后端分离脚手架我的看法是若依确实功能全自带权限、代码生成、定时任务而且也是SpringBootVueMyBatis的底子适合快速交付管理系统。但如果你是想搞清楚每一个技术点怎么工作还是得自己从零搭一遍。自己搭虽然多花几天时间但踩过的坑都会变成经验。1.3 项目结构与前后端边界怎么划分后端部分按经典分层来走Controller接收请求、Service处理业务逻辑、Mapper与数据库交互。绝大多数场景下这套分层就够了不要一开始就搬微服务那套东西过来复杂度会让你寸步难行。我通常还会加一层VO对象专门给前端返回数据用避免直接把数据库实体暴露出去。前端是标准的Vue工程views目录放页面components目录放复用组件router管路由api目录集中管理所有后端请求地址。页面和业务逻辑分开后后期加功能就是在views里加一个文件、在api里加一组方法的事维护成本明显低。前后端分离之后接口的“协议”必须提前定清楚。我在项目里定义了一个统一的返回体Result包含code、message、data三个字段成功和失败都走这一个格式。分页接口默认返回{ total, list }。这些约定一定要在联调之前定好不然后端返一个字段名前端等另一个字段名两边都在互相猜项目进度全耗在这上面了。2. 数据库设计与后端核心实现2.1 核心表结构资产、分类、借还记录、日志数据库设计是整个系统的地基表结构如果一开始没设计好后面写接口、写页面都会很别扭。我的核心表一共五张每一张都有明确职责。用户表sys_user存账号、密码、姓名、角色、状态。密码存的是BCrypt加密后的密文绝对不能明文落库。角色字段我用简单的admin和user区分管理员和普通员工第一版不把权限做得太复杂够用就行。资产分类表asset_category支持父子级分类比如“电子设备”下面可以再分“笔记本电脑”“显示器”“打印机”。用parent_id做自关联0表示顶级分类这样前端可以做无限级分类的下拉选择。资产表asset是核心表字段包括资产编号asset_no、名称name、分类ID、规格型号spec、单价price、购买日期、当前状态、使用人ID、存放位置、备注。其中asset_no是唯一值给每台设备做唯一标识可以按“设备类型缩写日期序号”生成比如PC-20240115-001。状态字段用整数表示1在库、2已借出、3维修中、4已报废写接口时直接按数字查不搞复杂的枚举表。借用归还记录表asset_borrow_record记录谁在什么时间借了哪台资产什么时候归还。业务上要保证同一时间一台资产只能被一个人借用所以资产状态和记录的状态必须联动更新加事务控制。审计日志表sys_log记录用户的关键操作包括操作人、接口路径、请求参数、IP地址、操作时间。这个表在排查问题上价值极高比如有人改错了资产信息翻日志能定位到具体是谁、什么时候、改了啥。建表时还有几个容易忽略的点字符串字段统一用utf8mb4字符集避免中文和表情符乱码asset_no和category_id、status这些高频查询字段要建索引所有业务数据表都带上create_time和update_time排序、断问题都用得上。2.2 SpringBoot项目搭建与关键配置我用Spring Initializr创建项目JDK用的1.8SpringBoot版本选了2.7.x。为什么不用3.x因为3.x要求JDK17起很多公司生产环境还是JDK8而且部分旧中间件对SpringBoot3的兼容还没完全跟上。做这类管理系统稳定第一版本不是越新越好。pom.xml里核心依赖就几个spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java再加lombok减少样板代码、hutool做工具类。JWT认证我用的是jjwt轻量够用。application.yml是整个后端配置的核心我贴一下关键部分server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/asset_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.asset.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl连接串里的serverTimezone一定要加不加的话高版本MySQL驱动会报时区错误。map-underscore-to-camel-case开启后数据库字段create_time就能自动映射到实体类的createTime省去写一堆resultMap的麻烦。log-impl配置成StdOutImpl可以在控制台直接打印SQL开发期排查问题离不开它。统一返回体ResultT很简单就是一个泛型类加上几个静态方法public class ResultT { private int code; private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.setCode(200); r.setMessage(success); r.setData(data); return r; } public static T ResultT error(int code, String message) { ResultT r new Result(); r.setCode(code); r.setMessage(message); return r; } }再配一个RestControllerAdvice全局异常处理器业务代码里只管抛异常统一由它转换成语义明确的JSON返回。这样前端拿到code ! 200时统一弹出message交互体验会好很多。2.3 MyBatis使用中的三个关键点分页、缓存、动态SQLMyBatis是这项目里数据访问的核心但很多新手使用时会踩坑。我重点说三个使用频率最高的点。分页插件PageHelper是实际项目中必装的组件。在pom.xml里引入pagehelper-spring-boot-starter配置一个helper-dialect: mysql即可。用法很简单PageHelper.startPage(pageNum, pageSize); ListAsset assetList assetMapper.selectByCondition(asset); // 紧跟查询 PageInfoAsset pageInfo new PageInfo(assetList);注意一个原则PageHelper.startPage()后面必须紧跟第一条查询语句中间不能夹其他业务逻辑否则分页可能作用到错误的SQL上。它的原理是通过MyBatis拦截器捕获下一条执行的SQL自动拼上LIMIT ? OFFSET ?非常方便但也容易误用。MyBatis缓存一级缓存是SqlSession级别的同一个会话里重复执行相同查询不会重查数据库二级缓存是命名空间级别的默认不开启。我在公司这种数据一致性要求比较高的场景里选择了关闭二级缓存mybatis: configuration: cache-enabled: false关闭的原因很简单缓存刷新时机不好控制资产状态一变其他会话查到的还是旧值最后用户看到的和数据库实际记录的不一致这种“幽灵数据”问题比慢查询更难排查。动态SQL是MyBatis最实用的能力。资产列表页有多个筛选条件名称模糊查询、分类ID、状态、使用人ID条件可能传也可能不传。用where标签可以自动处理多条件拼接避免手工拼SQL时多出一个AND的尴尬select idselectByCondition resultTypecom.example.asset.entity.Asset select * from asset where if testname ! null and name ! and name like concat(%, #{name}, %) /if if testcategoryId ! null and category_id #{categoryId} /if if teststatus ! null and status #{status} /if if testownerId ! null and owner_id #{ownerId} /if /where order by create_time desc /selectif标签配合where标签过滤条件有没有都无所谓SQL都能正确执行。批量操作比如批量删除就是foreach标签遍历List生成IN (...)这个用得也很多。再补充一个XML编写体验上的小事IDEA社区版不带XML SQL高亮和补全建议装一个“MyBatisX”或“MyBatis Log”插件。前者可以在Mapper接口和XML之间跳转后者能在控制台把带参数的SQL还原成可直接执行的完整语句对排查动态SQL问题特别有用。2.4 接口设计与轻量权限控制方案核心接口按业务模块划分资产模块就是以增删改查为主的分页接口GET /api/asset/page、POST /api/asset、PUT /api/asset、DELETE /api/asset/{id}。借还模块两个核心接口POST /api/asset/borrow发起借用、POST /api/asset/return归还资产。登录认证模块是POST /api/auth/login登录成功后返回JWT令牌。权限控制这块我没有上Spring Security全家桶而是用JWT HandlerInterceptor做了一个轻量拦截器方案。为什么这么选因为这套系统的权限模型很简单只有管理员和普通用户两级Spring Security配置起来反而显得沉重对新手也不友好。实现思路也不复杂登录成功后用jjwt生成token设置过期时间比如24小时。写一个AuthInterceptor拦截所有/api/**请求从请求头Authorization拿到token解析成功就放行失败则返回401。再配一个WebMvcConfigurer注册拦截器并放行登录接口和静态资源路径。“管理员才能操作”的校验就再写一个自定义注解RequireAdmin在编辑、删除、审批类接口上标一下拦截器中判断当前用户角色即可。这种方案在中小项目里非常实用简单直观代码量不到Spring Security的十分之一。3. Vue前端与页面交互落地3.1 前端环境准备Node、npm还有脚手架选择前端的技术栈同样很标准Vue 2 Element UI Axios Vue Router Vuex。这里要说明为什么我选了Vue 2而不是Vue 3。项目启动时Element UI对Vue 3的兼容还没有完全成熟Vue 3主打组合式API团队里有人不熟所以就沿用Vue 2这条稳妥路线。如果你是全新项目、团队对Vue 3组合式API有经验直接上Vue 3 Element Plus也完全可行。环境这块Node版本我建议用16.x LTS版本npm自然随Node安装。Windows下安装Node姑娘们去向官网下载安装包一路Next就行装完在命令行验证一下node -v npm -vVue CLI也要全局装命令是npm install -g vue/cli。如果网络不好或者安装慢先配置国内镜像源再装能省很多时间npm config set registry https://registry.npmmirror.com初始化项目就是经典的vue create asset-web交互式界面里选择Manually select features接着勾上Router、Vuex、Babel、Linter这几个选项。我习惯用Runtime Compiler模式虽然打包体积会大一点但胜在兼容性好。3.2 请求封装、路由守卫和项目骨架Axios封装是所有前端项目的地基。封装的两个核心目的请求发出前自动带上token响应回来后统一处理业务状态码避免每个页面里写重复的错误处理逻辑。import axios from axios; import { Message } from element-ui; import router from /router; const service axios.create({ baseURL: process.env.VUE_APP_BASE_URL || /api, timeout: 10000 }); // 请求拦截器 service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; } return config; }); // 响应拦截器 service.interceptors.response.use( response { const res response.data; if (res.code ! 200) { Message.error(res.message || 请求失败); if (res.code 401) { router.push(/login); } return Promise.reject(new Error(res.message)); } return res; }, error { Message.error(error.message || 网络异常); return Promise.reject(error); } );路由守卫确保未登录用户不能访问首页和业务页面这个在前端是必不可少的一道防线。虽然真正安全校验还是靠后端拦截器但前端做了之后用户体验会好很多不会被后端401弹窗轰炸。router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path ! /login !token) { next(/login); } else { next(); } });前端目录我按功能模块组织views/login放登录页views/dashboard放统计看板views/asset放资产列表和表单views/borrow放借还记录views/category放分类管理api/目录下对应建模块文件。3.3 资产列表与表单的实现要点资产列表页是使用频率最高的页面做得好不好直接决定用户对这个系统的评价。页面布局是顶部筛选区、中间表格、底部翻页器的经典结构。筛选区放名称输入框、分类下拉、状态下拉和查询/重置按钮表格核心列我固定为资产编号、名称、分类、状态标签、使用人、存放位置、购买日期和操作按钮。状态列的展示用Element UI的el-tag组件不同状态映射不同颜色在库是绿色、已借出是橙色、维修中是红色、已报废是灰色。这种视觉上的区分比“看文字”高效得多行政同事不用点进详情就知道设备什么状态。新增和编辑共用同一个对话框表单用el-dialog里的el-form实现。资产编号可以要求手动输入或由前端按规则生成注意加了编辑时的回显。分类选择器用el-cascader级联选择数据来自分类接口懒加载也可以但资产系统分类数据量少一次性全部查出更省事。表单校验用Element UI自带的rules规则比如名称必填、价格必须大于0。分页和搜索有一个需要小心的细节当你在第3页搜索一个条件后数据列表只有一页但页码还是停在3这时要用current-change把页码重置为1再发起请求。类似的还有编辑完数据后刷新列表应停留在当前页而不是跳回第一页这些交互细节决定了系统好不好用。3.4 借还流程和统计看板是怎么做的借出操作在前端是一个简单的弹窗选择当前在库状态的资产或扫描资产编号选使用人填备注确认后调用归还接口。归还操作更简单输入资产编号点归还即可系统自动找到未归还的记录并把资产状态改为在库。统计看板我用ECharts做两张图饼图展示不同分类的资产数量占比柱状图展示各状态下的资产数量。数据来源可以另写一个统计接口一次返回所需的聚合数据也可以前端拿到全部资产后在前端计算。资产量几千条以内的系统前端计算完全够用没必要为这个额外写复杂的SQL聚合。ECharts的引入方式是npm install echarts然后在组件里按需引入需要的图表类型。图表容器要设置明确的高度否则渲染不出来数据更新后要调用setOption而不是重新初始化实例否则会重复创建导致性能隐患。4. 打包部署全流程实战4.1 本地开发环境怎么跑起来拿到源码后第一件事是启动MySQL。Windows上安装MySQL 8.0的步骤是官网下载MySQL Installer安装时选择Server only按向导配置端口3306、设置root密码、选择字符集utf8mb4。安装完成后用客户端工具连接把项目里的sql/init.sql脚本执行一遍建库建表并插入默认管理员账号。后端启动前要确认application.yml里的数据库密码和自己的环境一致。在IDEA里直接运行主类看到SpringBoot启动成功的日志后可以在浏览器访问http://localhost:8080/api/auth/login测试接口连通性。前端启动前先npm install装依赖然后npm run serve。默认端口是8080、后端也是8080所以前端需要在vue.config.js配置代理让开发环境的/api请求转发到后端module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };这时访问http://localhost:3000登录页正常打开输入管理员账号密码能跳转到首页说明本地环境完全跑通了。4.2 前后端分别怎么打包后端打包非常简单在项目根目录执行mvn clean package -DskipTests打包完成后的jar包在target目录下文件名一般为asset-server-0.0.1-SNAPSHOT.jar。这个jar是SpringBoot的FatJar内置了Tomcat可以直接用java -jar运行。前端打包执行npm run build打包产物在dist目录是纯静态文件。里面包含index.html、JS、CSS、图片等资源扔到任何静态服务器上就能跑。很多人会把dist目录复制到后端SpringBoot的static目录下让后端统一提供页面和接口。我的建议是生产环境不要这么干。前端静态资源部署到Nginx/CDN上后端只做API服务这样前端发布时可以毫秒级切换后端升级也不需要把前端重新打包一遍两个服务有独立的生命周期。4.3 服务器部署Nginx反向代理与后端进程服务器上部署的核心是“Nginx托管前端静态文件并把/api请求反向代理给后端Java进程”。Nginx的关键配置如下server { listen 80; server_name your-server-ip; root /opt/asset-web/dist; index 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; } location / { try_files $uri $uri/ /index.html; } }try_files这个配置很关键如果Vue用了mode: history路由模式刷新页面时Nginx会先找对应路径的静态文件找不到就重写到index.html由前端路由接管页面。没有这行配置页面刷新大概率报404。后端进程部署建议用脚本管理写一个start.sh#!/bin/bash nohup java -jar /opt/asset-server/asset-server-0.0.1-SNAPSHOT.jar \ --spring.profiles.activeprod /opt/asset-server/logs/run.log 21 echo $! /opt/asset-server/pid.txt日志输出到文件后排查问题直接tail -f /opt/asset-server/logs/run.log看实时日志。生产环境的数据库连接建议单独写在application-prod.yml里专门配置生产库的连接串、密码和连接池参数避免把本地配置带到线上。如果是Windows服务器可以用任务计划程序或者写一个.bat脚本启动jar。团队有条件的话搭一套Jenkins自动化部署后端用mvn package构建、前端用npm run build构建然后通过SSH或脚本把产物分发到服务器整个过程半个小时就能搞定后面每次发版就变成点一次按钮的事。4.4 生产环境MySQL参数怎么调部署到生产环境后MySQL有几个参数值得调整。第一是字符集和排序规则建库时要显式指定utf8mb4和utf8mb4_general_ci第二是连接数默认的151在小团队够用但如果人数再多一点可以在my.ini里调到500第三是sql_mode建议去掉ONLY_FULL_GROUP_BY否则一些低版本的统计查询会报错但这个要看项目里SQL的写法。MySQL安装完还要注意一个安全问题root账号默认只允许本机连接。如果应用服务器和数据库不在一台机器上就得创建一个专用账号并授权远程访问。这个操作建议用最小权限原则——只给这个账号需要的库的权限不要直接授权所有库。不然一旦应用被入侵整个数据库都暴露了。5. 高频问题与排查技巧实录5.1 常见问题速查表做这个项目从开发到上线的过程中我整理了一批高频问题基本覆盖了前后端分离项目里最有代表性的坑。问题现象排查思路解决方法后端启动报数据库连接失败检查账号密码、连接串、MySQL服务是否启动确认连接串加serverTimezoneAsia/Shanghai测试MySQL端口是否开放前端接口全部404确认代理配置和baseURL开发环境配vue.config.js代理生产环境配Nginx的location /apiMyBatis查询结果全是null字段映射问题开启map-underscore-to-camel-case或写resultMapPageHelper分页不生效startPage和查询中间夹了其他逻辑保证startPage后面紧跟第一条要分页的SQL前端刷新后页面404Vue history路由没有fallbackNginx加try_files $uri $uri/ /index.html;列表中文显示乱码数据库字符集和连接字符集不一致统一使用utf8mb4连接串加characterEncodingutf8npm install特别慢默认源访问慢切换为国内镜像源SQL执行后想打印参数不知道实际执行语句用MyBatis Log插件还原完整SQL修改资产状态后列表数据不对可能命中了二级缓存关闭二级缓存或手动清理缓存这个表里大部分问题我都实际遇到过尤其是分页和路径的问题出现的频率最高。第一次遇到的时候会卡很久但等你知道原因之后再遇到基本就是看一眼就秒懂。5.2 项目中沉淀的几个独家避坑经验第一数据库字段命名统一用下划线实体属性统一用驼峰开启自动映射后两者完美衔接。项目里如果出现个别字段不自动映射先检查命名和映射配置不要急着写resultMap。resultMap不是不能用但能少写就少写代码能省则省。第二资产编号一定要做唯一约束并处理并发问题。如果两人同时提交相同编号的资产数据库唯一索引会挡住后插入的那条但给用户的体验应该是“编号已存在请更换”而不是直接报SQL异常。我在新增接口里先用编号查一次再让数据库的唯一索引兜底双保险。第三XSS安全过滤必须做但不能影响正常使用。我在后端加了一个全局过滤器对表单里提交的敏感字段做HTML标签和特殊字符过滤。处理上传文件场景时要特别小心不能对整个文件内容做过滤只处理文件名等文本字段避免把PDF或图片这类二进制内容弄坏。过滤规则宁可保守一点也不要让用户输入的内容能被当作脚本执行。第四审计日志要尽早加。第一次没加日志线上有人误改了资产状态后完全不知道谁干的只能看数据库的更新时间蒙。后来加了sys_log表并写了一个LogInterceptor在Controller层自动记录关键接口的调用者、参数和耗时。这个表带来的价值远超那点开发时间出了问题一小时能定位不用再跪求DBA翻binlog。第五前后端联调阶段最忌讳直接开写。先一起把接口文档过一遍字段名、类型、嵌套结构都对齐了再动手。很多返工不是因为谁技术不行只是因为两边理解的不一样。接口文档可以用Swagger也可以简单用一份Markdown关键是双方认可。这套系统做下来最大的体会是前后端分离的学习曲线不在某个单一框架上而在“连接”上。数据库怎么设计、后端怎么把数据变成接口、前端怎么把接口变成页面、部署时怎么把两边拼成一个整体每一环都有属于自己的坑。把这条链路完整走过一遍之后再看其他管理系统项目基本都能一眼看出它的架构和实现思路。后续如果还想继续扩展可以考虑给资产加二维码标签打印、增加备份数据库的定时任务、对接企业微信通知借还提醒这些方向底层这套框架完全撑得住。
返回列表