ARTICLE DETAIL

资讯详情

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

Spring Boot + Vue家庭维修系统:源码部署与前后端联调实战

Spring Boot + Vue家庭维修系统:源码部署与前后端联调实战 最近在帮别人整理一套“基于Spring Boot Vue的Web家庭设备维修服务系统”光是看标题就知道这不是一个只能跑个登录页的玩具项目而是包含用户下单、维修工接单、管理员派单、服务评价、维修进度跟踪等完整业务流程的企业级教学项目。很多人拿到源码后第一反应是打开IDEA直接运行结果被数据库连接失败、前端端口冲突、登录接口404、跨域报错这些问题搞得心态崩掉。这套项目如果只看代码不读文档确实容易绕晕但只要把“源码结构、部署配置、接口调用链路”这三条线理顺一个晚上跑起来是完全可能的。这篇文章就是我整理这套系统时踩过的坑和梳理出的关键点。我会从项目背景、源码阅读顺序、配置解析、本地联调、服务器部署、常见问题排查这几个角度完整讲一遍。如果你准备拿它做毕业设计、学习前后端分离项目或者想部署给小型维修公司当内部工具用照着这篇文章的思路走基本不会迷路。1. 这个系统到底在解决什么问题先说业务。家庭设备维修服务系统的核心不是“能登录”这么简单而是把用户报修、维修人员派单、服务评价这三段流程串起来。没有系统的时候维修订单记在纸质本子上客户打电话催维修师傅靠记忆排路线管理员根本不知道哪个师傅今天到底干了几单。有了这套系统用户通过手机或电脑提交地址、设备类型、故障描述、期望上门时间管理员在后台分配维修工维修工接单后更新状态“已接单、已上门、维修中、已完成”用户最后评价服务。整个过程有记录、可追溯维修响应效率和满意度都能明显提升。这类系统在国内高校的Java课程设计里也特别常见因为它的业务复杂度刚好够用有用户、订单、派单、评价、权限管理涉及多角色、多状态流转还能扩展支付、消息推送等模块。开发技术上又很主流Spring Boot负责后端接口Vue负责前端页面前后端分离部署时打成jar包加静态文件结构清晰。无论你是学生、个人开发者还是小团队想给物业公司做一套内部维修管理工具这个项目都很适合作为起点。1.1 家庭设备维修的核心业务闭环整个系统可以按照角色拆成三条线用户端注册登录、维护常用地址、提交报修单、查看当前进度、对完成的工单进行评价。维修工端查看被分配到的工单、改变工单状态、填写维修结果和耗材信息。管理员端管理用户、维修工、设备类别、审核和派单、查看所有工单统计、处理投诉或者异常工单。这三条线都围绕一个核心对象维修工单。工单数据贯穿整个项目所有功能设计本质上是围绕“工单状态”在做流转。状态一般包括待派单、已派单、维修中、已完成、已评价、已取消。前端页面根据状态显示不同按钮后端的Service层在不同状态之间做合法性校验。如果状态设计得乱后面写代码会非常痛苦所以源码里通常会在repair_order表和order_status_log表上做文章。order_status_log这个表很多人会忽略但它很重要。每条工单每次状态变化都记录一条日志谁在什么时间把工单从什么状态改成了什么状态全部存下来。这样做的好处是就算页面没显示管理员也能通过日志还原当时发生了什么用户投诉的时候不用看人脸色直接查记录。1.2 为什么选Spring Boot Vue这个组合后端用Spring Boot是因为它把Spring生态里那套复杂配置简化到了极致。内嵌Tomcat打一个jar包就能跑自动装配机制让数据源、MyBatis、Redis这些组件配置起来非常省事再加上Spring Security或JWT做登录认证社区资料多出问题基本都搜得到。对学习者来说Spring Boot几乎已经是Java Web项目的默认框架学它不会白费。前端用Vue看重的是组件化和开发效率。页面拆成头部组件、工单卡片组件、状态标签组件复用起来方便。Vue的双向数据绑定让表单交互写起来很直观配合Vue Router和Vuex或Pinia多角色页面之间切换和状态共享也清晰。前端的src/api目录里按模块封装接口页面里只调用方法不直接写axios请求这个习惯很多项目都在用。前后端分离带来的好处是开发和部署都更灵活。开发时前端用Node跑开发服务器后端单独启动通过代理把接口转发过去部署时前端打包成静态文件交给Nginx后端只提供一个对外接口服务。任何时候后端升级接口不需要重新发前端前端改版也不影响后端逻辑。这套模式已经是现在Web开发的主流学这个项目等于把主流工程实践完整走了一遍。1.3 适合哪些场景和学习路径如果你手头拿到的是带源码、部署文档、代码讲解文档的完整包你的第一条路是“按文档把系统跑起来”拿到原始状态再说。第二条路是“修改业务”加一个预约时间段、加一个材料清单表、把管理员派单改成维修工抢单。第三条路是“学原理”不看讲稿自己尝试画一遍系统涉及的表的字段和关系再看源码对比。第三条路比单纯读代码有用得多。很多人找工作面试时被问到项目经验不是因为没有做过项目而是因为只会照着文档念说不清楚为什么订单表要单独记录状态日志、为什么前端接口要统一封装。这些问题恰恰是这个项目里最有价值的东西。2. 源码到手怎么读先看结构再跑代码拿到一个你没有参与开发的项目最忌讳的事就是打开IDE盲猜。我一般会先看文档再看目录结构然后看配置文件最后才启动项目。这个顺序能省下大量排查时间。2.1 先分清“源码、部署文档、代码讲解”分别该怎么用这类完整的项目包通常包含四样东西源码压缩包、数据库脚本SQL、部署文档Word或Markdown、代码讲解文档。部署文档重点告诉你“怎么跑起来”讲解文档重点告诉你“代码是怎么设计的”。我建议先读部署文档把环境准备好让项目能在本机能跑再回头看代码讲解。如果顺序反了你连项目长什么样都不知道读代码时满脑子都是抽象概念。部署文档里最核心的内容是这几个JDK版本、MySQL版本、Node版本、数据库创建语句、配置文件改动位置、默认账号密码。很多部署文档是作者在自己电脑上写的路径和账号密码都是他自己的你复制的时候不能直接抄要改成你自己的。比如文档里写spring.datasource.password123456你的MySQL密码是root123就需要替换这不算文档错误是你没读仔细。2.2 后端工程的目录结构长什么样典型的Spring Boot后端结构长这样repair-server ├── src/main/java/com/repair │ ├── common │ │ ├── Result.java // 统一响应包装 │ │ └── BusinessException.java // 业务异常 │ ├── config │ │ ├── CorsConfig.java // 跨域配置 │ │ ├── MybatisPlusConfig.java // 分页插件 │ │ └── WebMvcConfig.java // 静态资源映射 │ ├── controller │ │ ├── AuthController.java │ │ ├── RepairOrderController.java │ │ └── AdminController.java │ ├── service │ │ ├── RepairOrderService.java │ │ └── impl/RepairOrderServiceImpl.java │ ├── mapper │ │ └── RepairOrderMapper.java │ ├── entity │ │ └── RepairOrder.java │ ├── security │ │ └── JwtUtil.java │ └── utils ├── src/main/resources │ ├── mapper │ │ └── RepairOrderMapper.xml │ ├── application.yml │ └── application-dev.yml └── pom.xml读后端代码的顺序我建议从Controller进。Controller是入口能通过接口定义知道系统对外提供哪些能力。看到Controller之后去Service找业务逻辑再看Mapper和XML了解SQL是怎么写的。Entity类里的字段对应数据库表如果某个字段在多个接口里重复出现很可能是表设计时就已经定好了不要随便改。2.3 前端工程的结构和阅读顺序前端一般叫repair-web目录典型结构如下repair-web ├── src │ ├── api │ │ ├── auth.js │ │ ├── order.js │ │ └── user.js │ ├── assets │ ├── components │ ├── router │ │ └── index.js │ ├── store │ │ └── user.js │ ├── utils │ │ └── request.js │ ├── views │ │ ├── admin │ │ ├── user │ │ └── worker │ ├── App.vue │ └── main.js └── package.json读前端代码先看utils/request.js因为所有接口请求都从这里经过。再看router/index.js理解不同角色如何跳转到不同页面。然后看store里面的用户状态保存逻辑。最后再按页面看api模块和后端接口如何对应。前端如果出现“页面按钮点了没反应”多半不是后端接口挂了而是前端记得缓存状态没清理或者接口路径写错。2.4 数据库设计里的几个关键点家庭设备维修系统的表数量一般不多核心表大概有这些表名主要字段说明userid, username, password, real_name, role, phone用户、维修工、管理员都在这张表通过role区分addressid, user_id, detail, contact_name, contact_phone用户常用地址device_categoryid, name, price_rule设备类型比如空调、热水器、冰箱repair_orderid, order_no, user_id, worker_id, category_id, status, appointment_time, description, create_time维修工单主表order_status_logid, order_id, from_status, to_status, operator_id, create_time状态流转日志evaluationid, order_id, user_id, rating, content, create_time用户评价数据库脚本导入后重点去看repair_order表的索引和order_status_log表的外键。如果表没有设置合适的索引等数据量到三万条以上按状态筛选工单会明显变慢。很多课程设计项目数据量小这一点平时根本感觉不到但生产环境很可能被用户“卡爆”所以这个项目里的索引和联合查询值得当成实战经验来学。3. 部署文档里没写清楚的配置细节每套项目的部署文档都不一样但核心配置差异不大。这里把最通用的部署要点拆开讲你可以对照自己的项目版本微调。3.1 本地开发环境版本怎么选我现在整理这套项目时后端是Spring Boot 2.7、前端是Vue 2采用的是一套很稳的经典组合。如果你拿到的是新项目可能是Spring Boot 3.x配Vue 3那要注意JDK版本必须是17或更高MyBatis相关依赖也要换成适配3.x的版本。环境版本参考如下软件推荐版本备注JDK1.8 或 17老项目用1.8新项目看pom文件Maven3.6.3不要在IDE里换默认版本MySQL5.7 或 8.0数据库脚本要兼容Node.js16.18 或 18太新的Node有时会报OpenSSL错误npm8随Node一起装Nginx1.20生产部署用这里有个很典型的坑老项目里用的是javax.servletSpring Boot 3.x里变成了jakarta.servlet。如果你的项目依赖是javax说明它运行在Java 8环境你偏要用Java 17去跑大概率启动失败。反过来Spring Boot 3的项目用Java 8也会报错。所以先看pom.xml再选JDK别让IDE的自动检查告诉你“版本都不对”时才发现。3.2 数据库初始化和导入顺序项目包里的一般是repair_db.sql文件。导入前要确定字符集。如果SQL文件里没有指定先在Navicat或者命令行创建数据库时指定CREATE DATABASE IF NOT EXISTS repair_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE repair_db; SOURCE /你的路径/repair_db.sql;导入完成后立刻检查三件事第一表数量是否和文档说明一致第二user表里有没有初始的admin账号第三外键是否正常。如果导入时出现重复键或字段长度不够通常是本机MySQL版本差异导致不用慌张查看第一个报错位置手动调整SQL再导一次就行。特别注意不要在导入数据库前直接启动后端。否则Spring Boot在启动时检测不到数据源会直接报错退出。很多项目的部署文档第一句就是“验证环境”但新手最容易跳过这一句。3.3 后端配置文件逐行拆解后端最重要的配置文件是application.yml。我整理过一份比较典型的配置用来说明每个配置项是干什么的server: port: 8080 servlet: context-path: / spring: datasource: url: jdbc:mysql://localhost:3306/repair_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl repair: jwt: secret: your-secret-key expire-days: 7 upload: path: /home/repair/upload很多项目会把不同环境的配置拆成application-dev.yml和application-prod.yml。本地用dev生产用prod。切换环境时只需要启动命令加上--spring.profiles.activeprod。注意repair.upload.path这个路径决定了上传的维修照片保存在哪里。Windows本地可能是D:/repair-uploadLinux生产环境建议用绝对路径/home/repair/upload并且在Linux下确认这个目录存在且有写权限。3.4 前端环境变量与代理配置前端开发时无法直接把请求发给后端的8080端口因为浏览器有跨域限制。项目里通常会用Vue CLI的devServer代理规避。在vue.config.js里会有类似的配置module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }这个配置的意思是前端页面里所有以/api开头的请求都会被Node开发服务器转发到http://localhost:8080并且把/api前缀去掉。所以后端接口实际是/login前端只需要请求/api/login。有的人会问为什么生产环境没有Node生产环境我们把代理这件事交给Nginx做到时候也是反向代理同一个道理。理解这一点后面部署就不会觉得怪。4. 本地联调流程与核心功能实现思路4.1 启动后端和前端的具体顺序本地联调的标准顺序是导入数据库脚本。修改application.yml里的数据库账号密码。启动后端等待Spring Boot日志出现Started。启动前端执行npm install再执行npm run serve。打开浏览器访问http://localhost:8081。后端启动时如果日志里只有Tomcat started on port(s): 8080说明后端没问题前端启动时如果提示App running at说明前端载体已经就绪。此时试试登录默认管理员的账号密码。登录成功后在浏览器开发者工具里看Network面板Requests中能看到请求路径、响应耗时、状态码。这一步是联调时最推荐的调试习惯比盲猜靠谱得多。4.2 维修工单状态机的代码实现这个系统里最值得讲的业务逻辑是状态机。要时刻防止“已完成”的工单被改回“维修中”之类的非法操作。很多老代码只在按钮上做了状态判断后端接口没有校验结果用户在页面点一下请求服务端照样接收了数据直接乱套。我建议在后端Service层显式判断状态public void updateOrderStatus(RepairOrder order, RepairOrderStatus targetStatus, Long operatorId) { RepairOrderStatus current RepairOrderStatus.valueOf(order.getStatus()); boolean allowed current.canTransitTo(targetStatus); if (!allowed) { throw new BusinessException(当前状态不允许变更为 targetStatus.getDesc()); } order.setStatus(targetStatus.getCode()); repairOrderMapper.updateById(order); OrderStatusLog log new OrderStatusLog(); log.setOrderId(order.getId()); log.setFromStatus(current.getCode()); log.setToStatus(targetStatus.getCode()); log.setOperatorId(operatorId); statusLogMapper.insert(log); }配合一个枚举来维护合法的状态转移关系public enum RepairOrderStatus { PENDING(PENDING, 待派单), ACCEPTED(ACCEPTED, 已接单), IN_PROGRESS(IN_PROGRESS, 维修中), COMPLETED(COMPLETED, 已完成), EVALUATED(EVALUATED, 已评价), CANCELLED(CANCELLED, 已取消); public boolean canTransitTo(RepairOrderStatus target) { switch (this) { case PENDING: return target ACCEPTED || target CANCELLED; case ACCEPTED: return target IN_PROGRESS || target CANCELLED; case IN_PROGRESS: return target COMPLETED; case COMPLETED: return target EVALUATED; default: return false; } } }这样写的最大好处是以后改业务规则只改枚举不需要翻遍所有Controller。比如要加一个“客户取消后管理员可以重新派单”的规则在枚举里增加一个从CANCELLED到PENDING的合法路径即可。代码量少还不容易漏改状态判断。4.3 前端请求封装和登录鉴权前端utils/request.js通常会对axios做统一封装核心逻辑是每次请求前从localStorage取token加到请求头拿到响应后统一处理业务码比如后端返回Result.success或Result.fail遇到401就跳回登录页。import axios from axios import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(repair_token) if (token) { config.headers[Authorization] Bearer token } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 200) { if (res.code 401) { router.push(/login) } return Promise.reject(new Error(res.message || 请求失败)) } return res.data }, error { return Promise.reject(error) } ) export default request在页面里调用就非常简洁了import request from /utils/request export function submitOrder(data) { return request({ url: /repair/order, method: post, data }) }这里有一个经验不要把接口地址写死在页面里。后端路径一旦改或者要统一拼接前缀你去每个页面里找接口字符串能找一晚上。统一封装在api/order.js这种模块里所有人看代码都舒服维护也简单。5. 生产部署实操Linux Nginx Jar本地跑通只能算第一步真正考验人的是生产部署。部署方式有很多Docker、系统服务、普通jar后台运行。这里讲最常见的Linux Nginx jar组合适合项目体量不大、未来想平滑扩展的情况。5.1 后端打包与后台运行先在本地执行Maven打包命令mvn clean package -DskipTests打包成功后target目录下会生成一个jar包比如repair-server-0.0.1-SNAPSHOT.jar。把这个jar上传到服务器的/home/repair/目录然后使用如下命令启动cd /home/repair nohup java -jar repair-server-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod app.log 21 这里nohup命令的意思是让程序挂后台运行日志输出到app.log。启动后立刻执行tail -f app.log观察有没有异常。很多新手在这里犯错命令运行后再按CtrlCsession结束程序也停了。用nohup ... 之后正常关掉终端不会影响它但是如果你的部署过程还有别的后台任务还是建议用systemd管理服务那样更规范。如果你要在服务器上更新版本先停掉旧进程再启动新进程。找进程方式ps -ef | grep repair-server kill 进程ID5.2 前端打包与Nginx静态资源配置前端打包命令是npm run build执行完成后项目根目录下会多一个dist文件夹。把dist里的所有文件上传到服务器的/home/repair/web目录。然后在Nginx配置里添加一个Server块server { listen 80; server_name repair.example.com; root /home/repair/web; 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 /upload/ { alias /home/repair/upload/; } location / { try_files $uri $uri/ /index.html; } }这个配置里最关键的是try_files $uri $uri/ /index.html;。因为Vue Router默认使用History模式用户访问/user/order/123时Nginx在静态目录里找不到这个文件如果没有try_files回退就会返回404。加上try_files后请求会被重新落到index.htmlVue路由再接管页面渲染。location /api/的代理也比较讲究。我这里的后端接口路径是/api/xxx所以proxy_pass http://127.0.0.1:8080;不带URI。如果后端接口本身没有/api前缀要在Nginx这层做路径重写具体写法要看你的项目约定不要照抄网上模板就完事。5.3 文件上传路径和图片访问的配置维修系统一定会涉及上传故障照片所以Nginx和Spring Boot都要处理好上传路径。Spring Boot这边通常已经做了静态资源映射Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadPath /); } }这样上传后的文件比如/home/repair/upload/2025/abc.jpg可以通过http://服务器IP/upload/2025/abc.jpg访问。Nginx里的location /upload/会先拦截请求直接交给静态文件目录不走后端。如果没有这层Nginx配置用户访问图片会走Spring Boot虽然也能显示但性能差不少。部署到生产环境后一定要检查上传目录的权限。很多Linux服务器上Java进程运行在tomcat用户或root用户下如果目录是755权限进程没有写权限上传接口会报“无法创建目录”。最简单的方式mkdir -p /home/repair/upload chmod -R 755 /home/repair/upload6. 常见问题与排查技巧实录这一部分是我在实际踩坑过程中总结出来的不一定每一个项目都会遇到但命中率极高。6.1 启动报错速查表报错信息或现象常见原因处理建议Failed to configure a DataSource数据库连接配置错误或数据库没启动检查application.yml里的url、账号、密码确保数据库已启动Port 8080 was already in use端口被占用lsof -i:8080或netstat -ano查占用换端口或杀进程前端页面请求接口返回404前端代理没生效或接口路径不对看Network请求路径确认是否走了/api代理登录接口跨域后端CorsConfig没配置或配置了错误的允许源加上CorsConfig或用Nginx统一反向代理Vue项目启动报Node Sass版本错误依赖版本和Node版本不匹配删除node_modules和package-lock.json重新npm install部署后访问接口502后端没启动或防火墙禁止了端口curl http://127.0.0.1:8080测试后端再检查安全组规则6.2 几个容易忽略但特别影响体验的细节第一MyBatis的resultMap不要滥用。如果你的表字段和实体类字段能用map-underscore-to-camel-case自动映射就不用每个查询写一长串resultMap。但如果碰到带有order_no这种下划线字段还需要确认驼峰转换配置是否开启否则查出来的对象orderNo一直为null排查起来会很费劲。第二不要在生产环境开启SQL日志。开发环境常用的:configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl在正式生产环境建议去掉因为控制台打印的SQL会非常频繁容易把日志文件撑爆还会泄露表结构信息。如果你需要排错可以设置Spring Boot的logging.level只打印某个Mapper包下的SQL不要全局打印。第三数据库时间字段要留意时区。在连接串上加上serverTimezoneAsia/Shanghai是一个简单又省事的方法。项目里的create_time如果全部用数据库now()生成不同时区服务器之间会观察到好几个小时的时间偏差。如果前端页面显示的时间和实际差8小时基本就是时区问题。第四修改前端代码后没有重新打包。很多部署现场是前端改了页面问为什么线上没变化最后发现根本没执行npm run build还一直在用老旧的dist文件。这不算技术问题但是最容易出错的流程问题。6.3 如果只有jar包没有源码怎么定位问题有时候拿到手的是一个可运行的jar包没有源码。想排查接口返回的逻辑可以用反编译工具把jar包里的class反编译成Java源码结合部署文档里的接口说明去对照。比如JD-GUI这类工具能直接打开jar文件浏览类结构。不过这是辅助手段反编译出来的代码可读性参差不齐变量名、注释全都会丢真正能用来修改项目并重新打包工程量很大。我更推荐的做法是先用java -jar启动然后通过接口调用和日志来观察行为。开启--debug或者打印请求日志不一定需要源码就能定位大部分问题。比如接口返回500日志会直接告诉你异常发生在哪一行哪个MyBatis的XML文件写法不对再看对应的表结构就能推断出问题逻辑。能不动一个线上正在运行的项目就别去动它这是运维的基本心态。最后再分享一个我个人的习惯。每次拿到一个像样的全栈项目我一定会在跑通之后重新看一遍数据库表关系手动把表名、字段名、业务含义画成表格记下来。因为代码可以重构文档可以过期但表结构里的业务设计往往是最稳定的一层。这个维修系统的核心就是从一张维修工单开始把人和设备、服务流程、评价反馈全部串起来。把这件事想明白了不管你是用它交作业、改造上线还是面试时被问到项目细节都能讲得有理有据比死记硬背源码强得多。
返回列表