拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Spring Boot + Vue家庭设备维修服务系统:源码剖析与部署实战

Spring Boot + Vue家庭设备维修服务系统:源码剖析与部署实战

家庭设备维修服务系统这类项目,这几年在Java课程设计和毕业设计里出现的频率相当高。原因也简单:业务场景足够贴近生活,需求容易理解,前后端交互清晰,技术栈又能完全踩中企业招聘JD上的主流关键词。我前前后后帮人review过不少类似的系统,也实际动手搭过完整项目,今天就把这套基于Springboot + Vue的Web家庭设备维修服务系统,从源码设计、核心代码讲解到部署文档整理成一篇完整的实操记录。无论你是准备拿它做课程设计,还是想学习前后端分离项目的完整落地流程,这篇文章都够你用。

先把这个项目的定位说清楚。它本质上是给小区物业或第三方维修平台做的一套线上报修管理工具,用户在小程序或网页上提交维修单,管理员派单,维修工接单处理,全过程在线流转,替代传统电话报修加纸质登记的模式。系统的核心价值在于三个角色之间的信息同步:业主不用反复打电话催进度,维修工不用等客服口头派单,管理员能实时看到所有工单的状态和完成率。这套逻辑放到哪个维修场景里都成立,所以项目的业务模型具有很强的可复用性。

下面我按自己做项目时的推进顺序,把整套东西拆开来讲。

1. 项目核心需求与整体设计拆解

1.1 这个系统到底要解决什么问题

做项目之前先别急着写代码,先把业务痛点梳理清楚。传统的家庭设备维修流程是这样的:业主发现水管漏水或者电路故障,打电话给物业,物业记在纸上,然后通知维修师傅,师傅再抽空上门。这个流程最大的问题在于信息的断裂和黑盒化:业主不知道维修师傅什么时候来,维修师傅不知道除了这一单之外还有没有顺路能一起处理的单子,物业经理统计月度维修工作量只能靠翻记录本。

这套维修服务系统要做的就是把这些流程线上化。业主在线提交报修单,填写故障类型、设备位置、问题描述,可以上传照片;维修工可以看到分派给自己的任务,处理完以后填写维修结果和材料费用;管理员负责审核用户、分配工单、查看统计数据。整个过程的状态是透明可追踪的,每一单从提交到完成都能回溯,这就是它相比传统模式最本质的改进。

理解了这一点,你再看网上流传的各种版本源码,就会发现它们的功能模块再怎么变,核心都是围绕“工单状态流转”这个主线来设计的:待派单、已接单、维修中、已完成、已评价。任何状态设计得混乱的版本,业务逻辑十有八九是讲不通的。

1.2 角色划分与功能模块梳理

这套系统的角色一般分为三类:普通用户(业主)、维修工、管理员。也有部分版本会把维修工和管理员的权限合并,但正规的设计里两者一定要分开,因为它们的操作对象完全不一样。

普通用户端的核心功能是报修全流程:提交报修单、查看自己历史工单列表、查看工单处理进度、对完成的工单进行评价。这里要注意一个细节,用户只能看到自己创建的工单,不能看到别人的,所以所有的查询都要带上当前登录用户ID作为条件,这既是业务逻辑要求,也是数据隔离的基本安全要求。

维修工端的核心功能是接单和处理:查看分派给自己的工单列表、接单或者拒单、填写维修记录(包括故障原因、维修方式、材料费用)、修改个人状态(空闲/忙碌)。有些项目里还做了维修工的技能标签,比如擅长电路还是水暖,方便管理员派单时做匹配,这个功能可以作为加分项。

管理员端是最重的,包括用户管理(审核注册用户、禁用恶意用户)、维修工管理(新增维修工账号、分配角色)、工单管理(查看所有工单、派单给指定维修工、处理投诉)、数据统计(按周/月统计工单量、完成率、客单价)。在实现的时候,管理员端的功能会直接决定这个项目的难度上限,如果你想拿高分,数据统计这块的建议是:一定要做图表可视化,而不是简单的表格罗列。

1.3 为什么选择Spring Boot + Vue这套组合

这个问题面试官或答辩老师一定会问,你自己心里也要有底。选择这套技术栈不是因为它新,而是因为它最适合中小型Web管理系统的开发效率要求。

先说Spring Boot。它解决了传统SSH/SSM框架最头疼的配置地狱问题,内嵌Tomcat容器,打包成jar直接跑,自动配置机制让开发初期几乎不需要关心Bean的装配细节。对于课程设计这个体量的项目来说,Sprng Boot能把你的精力从“怎么让项目跑起来”解放到“怎么把业务逻辑写清楚”上,这是它最大的价值。

再说Vue。Vue在前端框架里的定位是渐进式、轻量、上手快。相比React的学习曲线,Vue的模板语法、双向绑定、组件化开发方式更适合没有系统学过前端框架的人。特别是Vue配合Element UI这类组件库,做后台管理界面几乎是拼积木一样的体验,一个表格组件、一个表单组件、一个弹窗组件拼一拼,页面就出来了。再加上Vue Router做路由管理、Axios做HTTP请求,前后端通过JSON格式交互,清晰明了。

关键的是,这套组合也是目前中小型公司用得最多的技术方案之一。你把它完整做一遍,等于模拟了一次真实的企业开发流程。我在实际review项目的时候,最看重的是候选人能不能讲清楚前后端数据是怎么流通的、请求经过哪些层、状态码怎么约定——这些东西你在手写这套系统的过程中会自然形成肌肉记忆,比背一百道面试题都有用。

2. 源码结构解析与关键代码逻辑

2.1 后端项目结构与分层职责

拿到一套Spring Boot源码,第一件事不是急着跑起来,而是先看目录结构。正规的Spring Boot项目一定遵循分层的MVC架构,我用这套维修系统给你做一个标准的目录拆解。

src/main/java/com/example/repair/ ├── controller/ // 控制层:接收前端请求,返回JSON │ ├── UserController.java │ ├── OrderController.java │ └── AdminController.java ├── service/ // 业务层:核心业务逻辑处理 │ ├── OrderService.java │ ├── UserService.java │ └── impl/ ├── mapper/ // 数据访问层:MyBatis的Mapper接口 │ ├── OrderMapper.java │ ├── UserMapper.java │ └── RepairmanMapper.java ├── entity/ // 实体类:对应数据库表结构 │ ├── User.java │ ├── RepairOrder.java │ └── Repairman.java ├── config/ // 配置类:跨域、拦截器、JWT等 │ ├── WebConfig.java │ └── JwtInterceptor.java ├── common/ // 通用类:统一返回结果、异常处理 │ ├── Result.java │ └── GlobalExceptionHandler.java └── RepairApplication.java // 启动类

这套分层没有花活,但它是合理的。Controller只负责接收参数和返回结果,不写任何业务逻辑;Service里放真正的业务规则;Mapper只做数据库交互。好处是后期好维护、好测试、答辩的时候也容易讲清楚层次关系。

有一点我要特别强调:统一返回结果类是所有Controller的通行证。我见过不少项目每个接口返回的数据结构都不一样,有的返回Map,有的返回JSONObject,有的直接把实体类丢回去,前端联调的时候痛苦得要命。规范的做法是定义一个Result类,所有接口统一返回,比如:

@Data public class Result<T> { private Integer code; // 状态码:200成功,400业务错误,500系统错误 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(400); result.setMessage(message); return result; } }

前端拿到这个结构,直接判断code === 200就知道请求是否成功,不用关心数据格式千奇百怪的问题。这个习惯如果你从现在开始培养,后面做任何项目都会受益。

2.2 数据库设计与核心表结构

数据库设计是整套系统里最能体现功夫的环节。很多同学拿到源码第一步就跑程序,结果报错就懵了,因为表结构没看明白。我先帮你们把这套系统最核心的几张表拎出来。

核心表至少需要四张:用户表、维修工表、报修工单表、评价表。如果需要做管理员单独的表也可以,但很多项目直接用一张用户表加角色字段来区分,我建议用后者,省一张表不说,权限判断也统一。

用户表和维修工表结构比较常规,重点是字段的冗余设计。比如维修工表里可以直接冗余一个current_status字段,用0/1表示空闲/忙碌,这样管理员派单的时候就不用实时去算该维修工有几单在途,直接查这个字段就行,性能好且逻辑简单。这个思路在生产环境里叫“用空间换时间”,用适当的字段冗余简化查询逻辑。

报修工单表是整张表的核心,字段必须覆盖完整业务链路:

CREATE TABLE `repair_order` ( `id` int NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '工单编号', `user_id` int NOT NULL COMMENT '报修用户ID', `repairman_id` int DEFAULT NULL COMMENT '接单维修工ID', `device_type` varchar(20) DEFAULT NULL COMMENT '设备类型:水/电/暖/门窗等', `description` text COMMENT '故障描述', `image_url` varchar(255) DEFAULT NULL COMMENT '故障照片地址', `address` varchar(255) NOT NULL COMMENT '维修地址', `status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0待派单 1已接单 2维修中 3待评价 4已完成 5已取消', `appointment_time` datetime DEFAULT NULL COMMENT '预约上门时间', `create_time` datetime DEFAULT NULL COMMENT '提交时间', `finish_time` datetime DEFAULT NULL COMMENT '完成时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这个表的字段设计是有讲究的。状态字段是整套系统的灵魂,每一步操作其实都是在改变这个状态值。派单是把status从0改成1,接单是把1改成2,维修完成是把2改成3,用户评价后变成4。理解了这个状态机,你对整个项目的理解就通了一大半。

order_no这个字段容易被忽略,但它是工单的可视化标识。用户打电话咨询时不可能说“我那个ID为82的单子”,但是可以说“我那个编号RW20250115001的报修单”,这就是业务编号存在的意义。生成规则一般用日期加自增序号,写一个简单的工具方法就能生成。

2.3 核心业务流程的代码实现

讲几个最核心的业务逻辑点,这些也是代码讲解的重头戏,答辩的时候讲这些比讲CRUD有说服力得多。

第一个是报修单提交的完整事务链。用户提交报修信息的时候,前端传过来的数据里有设备类型、问题描述、图片等。你不要只在OrderMapper里做一个简单的insert,要想想业务上还有什么隐含的动作。比如我习惯在提交之后同时做两件事:生成工单编号、记录操作日志。工单编号可以在Service层生成后直接set进实体类,操作日志是为了后续追溯。

第二个是派单逻辑。管理员派单时,后端要做三件事:校验维修工状态是空闲、把维修工ID写入工单、把维修工状态置为忙碌。这三步必须在一个事务里完成,否则可能出现维修工被重复派单的情况。我的建议是在Service方法上加@Transactional注解,任何一步失败就整体回滚:

@Transactional public Result assignOrder(Integer orderId, Integer repairmanId) { // 1. 校验工单状态是待派单 RepairOrder order = orderMapper.selectById(orderId); if (order == null || order.getStatus() != 0) { return Result.error("工单状态不允许派单"); } // 2. 校验维修工状态 Repairman repairman = repairmanMapper.selectById(repairmanId); if (repairman == null || repairman.getCurrentStatus() != 0) { return Result.error("该维修工当前不可接单"); } // 3. 更新工单和维修工状态 order.setRepairmanId(repairmanId); order.setStatus(1); orderMapper.updateById(order); repairman.setCurrentStatus(1); repairmanMapper.updateById(repairman); return Result.success(null); }

你看这个逻辑,每一步都对上一个步骤的结果做了校验,这种“前置校验”意识是区分初级和中级开发者的重要分水岭。不要等数据已经错了才去兜底。

第三个是权限校验拦截器。前后端分离项目的接口默认都是裸奔的,任何接口任何人都能直接调用,所以要对需要登录才能访问的接口做拦截。常规做法是实现一个HandlerInterceptor,在preHandle方法里从请求头取token,解析出用户身份后再放行:

public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token != null && token.startsWith("Bearer ")) { String realToken = token.substring(7); // 解析token中的用户信息,写回request request.setAttribute("userId", JwtUtil.parseUserId(realToken)); return true; } response.setStatus(401); return false; } }

这里我踩过一个坑需要提醒你:如果拦截器把所有接口都拦截了,前端登录接口自己也被拦了,这就形成了死锁。解决办法是在WebConfig里注册拦截器时,用excludePathPatterns把登录、注册这类白名单接口排除掉。这也是一个很常被问到的面试点。

2.4 前端Vue工程的组织方式

Vue前端项目的结构,我建议你用Vue CLI或者Vite从零搭一个自己熟悉的,再去对比源码,这样源码头绪才理得清。典型的工程目录长这样:

src/ ├── api/ // 接口请求封装:每一个后端接口对应一个js文件 │ ├── user.js │ ├── order.js │ └── request.js // Axios实例封装,统一拦截器 ├── router/ // 路由配置:路径和组件的映射关系 │ └── index.js ├── store/ // 状态管理:Vuex/Pinia,存用户登录态等 │ └── index.js ├── views/ // 页面组件:一个文件夹对应一个页面 │ ├── login/ │ ├── user/ │ └── admin/ ├── components/ // 公共组件:表格、表单、弹窗等 └── App.vue

前端最核心的两个文件是request.js和router/index.js。request.js的作用是统一处理所有HTTP请求,比如在请求拦截器里自动加上token请求头,在响应拦截器里统一处理401跳转登录页,这样每个业务页面里调接口的时候就不用重复做这些逻辑了。router/index.js里要配合后端的角色做路由守卫,用户未登录就访问需要权限的页面时,直接重定向到登录页。

前端的API封装这一点,几乎没人会认真做,但它是项目整洁度的分水岭。每个页面直接从@/api/order.js里import方法调用,而不是在组件里满屏写axios.get(...),代码可读性完全是两个档次。我后来带前端实习生的时候,第一个要求就是API必须集中管理。

3. 部署文档实操:从零到系统能跑起来

3.1 环境准备与版本选型

部署环节是这套操作里最劝退新手的,因为版本不匹配会引发一堆莫名其妙的报错。先记下一套我自己验证过很多次、兼容稳定的版本组合:

组件版本说明
JDK1.8或11不要上17以上,部分老版本依赖会出兼容性问题
Maven3.6.x构建后端项目的依赖管理工具
MySQL5.7或8.0不要用5.5,utf8mb4支持不完整
Node.js14.x或16.x对应Vue CLI 4.x/5.x
Vue CLI4.5.x或5.x脚手架工具,也可以直接用Vite
Nginx1.20.x部署前端静态文件和反向代理

版本这里真的别贪新。很多同学一上来就装了最新的JDK 21,结果Spring Boot 2.7项目跑不起来,还以为是代码有问题,其实纯粹是版本兼容性。Spring Boot 2.x配JDK 8是最稳的组合,你先把项目跑通再考虑升级不迟。

另一个容易出问题的环境变量配置是MAVEN_HOME。如果命令行里mvn -v提示找不到命令,检查两件事:是否配置了Maven的bin目录到Path环境变量、是否配置了MAVEN_HOME变量指向Maven的安装根目录。国内下载Maven依赖慢的话,记得把Maven仓库地址换成国内镜像,具体在settings.xml里配置,这个操作用搜索引擎一搜就有,不展开了。

3.2 数据库初始化与配置文件修改

MySQL装好之后,第一步是创建数据库,然后导入项目里附带的SQL脚本。常规做法是用命令行先登录:

mysql -u root -p123456

登录后执行:

CREATE DATABASE repair_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE repair_system;

找到源码目录下sql/文件夹里的SQL文件,用source命令导入:

source D:/project/repair_system.sql;

导入完以后,可以验证一下表是否创建成功:SHOW TABLES;。

导入成功之后,去改后端的配置文件。Spring Boot的主配置文件是src/main/resources/application.yml,你需要改的就两处:数据库连接信息和服务器端口。关键配置如下:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/repair_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

这里要特别注意serverTimezone=Asia/Shanghai这个参数,不配置的话,数据库连接时会报时区错误,这是新手最常见的一个坑。useSSL=false是让本地开发时不要开启SSL握手,节省时间。密码改成你自己MySQL的密码。

3.3 后端打包与启动

后端项目的启动方式有两种:一种是在IDE里直接运行启动类,适合开发调试;另一种是打包成jar用命令行跑,适合部署和生产环境。两种方式都有必要学会。

开发调试直接找到RepairApplication.java,右键运行就行。如果控制台打印出Spring Boot的启动banner,最后看到“Started RepairApplication”,说明后端已经跑起来了,默认地址是http://localhost:8080。如果中间报错,百分之八十是数据库连接失败,回去检查用户名密码和数据库名字。

打包发布用Maven命令:

mvn clean package -DskipTests

-DskipTests的意思是跳过测试代码执行,否则Maven可能会尝试运行项目里的单元测试,测试环境没配好又会导致打包失败。打包成功以后,target/目录下会出现一个repair-system-0.0.1-SNAPSHOT.jar文件,然后就可以用java命令启动:

java -jar target/repair-system-0.0.1-SNAPSHOT.jar

如果线上服务器内存比较小,可以用-Xms和-Xmx来限制JVM内存分配:

java -Xms256m -Xmx512m -jar target/repair-system-0.0.1-SNAPSHOT.jar

这一步是为了防止默认的JVM最大内存占用太大,把服务器拖垮。

3.4 前端依赖安装、打包与Nginx发布

前端部分的操作分三步:安装依赖、本地跑通、打包部署。

下载前端项目代码后,在项目根目录下执行:

npm install

这一步会按照package.json里的依赖清单把项目需要的所有依赖包下载到本地的node_modules文件夹。npm install第一次跑通常会比较久,属于正常现象。如果中间报错,大概率是网络问题,可以换成国内的npm镜像源。安装成功后执行:

npm run serve

启动成功后控制台会显示访问地址,一般是http://localhost:8081。此时后端和前端都在本地,还需要配置代理让前端能请求到后端。在前端项目根目录下创建vue.config.js(Vite项目则是vite.config.js),配置跨域代理:

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

这里把前端的/api开头的请求代理到后端的8080端口,这样开发环境就不会有跨域问题。注意,后端接口路径如果原本就是/api/xxx,那代理后前端就写/api/xxx;如果后端路径不带/api前缀,那前端写请求路径时要记得手动拼上/api去匹配代理规则。

本地联调没问题之后,打包成静态文件:

npm run build

打包完成后会生成一个dist/目录,里面是压缩好的HTML、CSS、JS文件。这个dist目录需要交给Nginx做静态文件服务和反向代理。Nginx配置的关键内容:

server { listen 80; server_name localhost; location / { root /usr/share/nginx/html/dist; 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; } }

try_files $uri $uri/ /index.html这一行是Vue Router使用history模式时的标配,不加的话刷新子路由页面会出现404。还有一个细节要注意:proxy_pass http://127.0.0.1:8080;后面没加斜杠,这意味着/api/xxx会被完整转发给后端,如果后端接口本身带/api前缀,这个写法是对的。如果后端没有/api前缀,要改成proxy_pass http://127.0.0.1:8080/;,把/api从路径中消掉。

Nginx配置完成后执行nginx -t检查语法,然后nginx -s reload重载配置。浏览器访问http://localhost,如果能看到登录页面,说明整套部署流程已经通了。

4. 常见问题排查与避坑指南

4.1 数据库连接类报错

这类问题在启动阶段出现得最频繁,我把常见的几种情况直接列成速查表,你照着对就行。

报错信息原因解决方式
Access denied for user 'root'@'localhost'数据库用户名或密码错误检查application.yml里的账号密码
Unknown database 'repair_system'数据库不存在执行CREATE DATABASE语句创建库
Public Key Retrieval is not allowedMySQL 8.0的认证插件问题连接URL加allowPublicKeyRetrieval=true
The server time zone value is unrecognized时区未配置连接URL加serverTimezone=Asia/Shanghai
Table 'xxx' doesn't exist表名不匹配或数据库未导入执行SHOW TABLES确认表是否存在

尤其要警惕表名大小写问题。MySQL在Linux环境下默认是区分大小写的,Windows下不区分。如果你的SQL脚本建的表名是repair_order,代码里写的注解却是@TableName("RepairOrder"),在Windows上可能没问题,部署到Linux服务器上就直接报表不存在。所以在写代码时,表名统一小写,实体类用驼峰命名,并通过MyBatis-Plus的map-underscore-to-camel-case配置自动映射,避免手动映射出错。

4.2 跨域和接口联调问题

前后端分离项目最常见的坑就是跨域。你在前端页面上F12打开控制台,看到类似Access to XMLHttpRequest at 'http://localhost:8080/...' from origin 'http://localhost:8081' has been blocked by CORS policy这样的报错,就说明跨域配置没生效。

跨域的解决办法总共有三条路。第一条已经在前面提过,开发环境用vue.config.js的devServer代理。第二条是后端开启CORS配置,允许指定的来源访问:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8081") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowCredentials(true); } }

第三条是部署环境用Nginx反向代理,把前后端放在同一个域名下,从根源上解决跨域。这里我推荐优先用代理方案,因为CORS配置如果写法不当(比如allowCredentials(true)和allowedOrigins("*")不能同时使用),容易自己也踩进去出不来。

还有一个被忽视的联调问题,是前端登录成功后每次请求怎么携带token。Axios拦截器里这样配置:

// 请求拦截器 axios.interceptors.request.use(config => { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = 'Bearer ' + token; } return config; });

如果你发现后端接口总是报401,检查两个地方:第一,前端有没有把token存进localStorage;第二,请求头字段名是否跟后端读取的一致。我曾经见过有人后端取的头部叫Authorization,前端发的却叫authentication,这种问题光用眼睛看很难查出来,用开发者工具看网络请求的Headers是最快的。

4.3 部署环境下的其他疑难杂症

页面刷新404:原因在于前端使用的是Vue Router的history模式,Nginx没有配置try_files。解决方式已经在Nginx配置里写了,这一条务必好好记住。

图片上传后访问不到:用户提交报修单时可以上传故障照片,项目里通常会把图片保存到本地的某个目录下,比如/upload。如果你用Nginx做了前端静态资源托管,图片上传接口和图片访问路径容易出现错位,比如后端保存到D:/upload/xxx.jpg,但前端访问的时候是http://localhost/upload/xxx.jpg,因为Nginx默认只代理了/api路径,/upload路径没代理到后端。解决方式是在Nginx再加一条:

location /upload/ { proxy_pass http://127.0.0.1:8080; }

这里也顺便回答一个很多新手会问的问题:为什么图片不能直接放在前端项目的static目录里?因为前端打包以后每次重新发布都会清空dist目录,你上传的图片会被一起清掉,所以上传文件一定要落在独立的目录,最好是后端管理的目录或者对象存储服务里。

Spring Boot打包后文件上传路径问题:如果你用的是System.getProperty("user.dir")这种相对路径来保存上传文件,开发环境和打jar包运行后的环境可能不一致,导致图片找不到。推荐用绝对路径保存,并把上传目录配置到application.yml里,这样每次部署的时候可以灵活修改。

5. 二次开发与性能优化方向

5.1 业务功能如何扩展

如果你想把这套系统做得更有竞争力,我建议在完成基本功能之后沿着三个方向去扩展。

第一个方向是预约维修。当前系统一般是用户提交工单,维修工挤时间上门。如果加上预约时间功能,用户在下单时就可以选择未来三天内的某个时间段,维修工在接单时看到预约时间,能更好地规划自己的路线。这个功能改动量不大,只要在repair_order表加一个appointment_time字段,前端加一个时间选择器,后端在派单时校验一下预约时间即可。

第二个方向是消息通知。工单状态一变就通知相关人,这个在真实场景里非常重要。实现方式可以走简单路线:在状态变更的核心方法里,查一下用户手机号,调用短信服务商的HTTP接口发消息;或者更简单一点,做个站内信模块,用户在系统里能看到消息列表。从学习角度出发,站内信更合适,因为你不用依赖外部服务商,而且能顺带练习消息表的读写逻辑。

第三个方向是多小区/多网点支持。现在的模型是单维修点、单管理员。真实场景里一个物业公司可能管着好几个小区,每个小区配备不同的维修工。只要加一个community_id字段,然后所有查询都带上这个条件,系统就能从单点模型扩展成多点模型。这个扩展思路能体现出你对业务的理解深度,面试时讲出来是加分项。

5.2 从课程设计到生产环境的思维升级

项目做完不是终点,思考怎么让这个项目“看起来很专业”才是提升的关键。同样是课程设计,为什么有些人的项目看起来就像企业级产品,有些人的项目一看就是教学Demo?差别就在几个容易被忽略的细节上。

日志记录是第一个分水岭。业务系统里关键操作一定要打日志,比如派单操作、状态变更操作,日志里要能看出是谁在什么时间做了什么。没有日志的系统,线上出问题根本不具备排查能力。Spring Boot直接用@Slf4j注解,在关键业务方法里用log.info记录就行。

异常处理的统一是第二个分水岭。不要满Controller都是try-catch,正确的做法是抛出自定义业务异常,然后由全局异常处理器统一捕获转换。这样Controller看起来非常干净,业务代码里只表达正常流程,异常情况统一兜底。

输入参数校验是第三个分水岭。前端传上来的参数不能直接信任,手机号段要校验、工单状态要校验、空值要拦截。用@Validated注解加@NotNull、@Pattern这些约束,在校验失败时自动返回统一的错误提示,这也是面试时能聊半天的知识点。

做项目这件事,我一直认为做十个小项目不如把一个项目吃透。这套维修服务系统麻雀虽小五脏俱全,从前端交互到后端事务、从权限认证到部署发布,整套链路走一遍,你获得的不仅仅是答辩时能讲清楚一张流程图,而是真正建立起对Web前后端分离项目完整生命周期的体感。建议你拿到源码后,先按部署文档跑通,再一行一行读代码,最后自己动手把某个模块改掉重写一遍,这个过程里踩到的坑、想明白的原理,才是这门课程带给你最值钱的东西。

返回列表