每年毕业季都有不少人带着类似的标题来找我——"Java+SpringBoot家政服务平台""家政服务管理平台Web版"。说实话,这类题目在计算机毕设里属于标准意义上的"稳妥选择":业务场景清晰、用户角色明确、技术栈主流,不卷算法不碰硬件,认认真真做完,答辩的时候也能讲出东西。这篇就把我当时做这个选题时的完整思路、编码路径和踩坑记录摊开写出来。内容不是教科书式的步骤堆砌,更多是面向"我拿到这个题目之后,具体该按什么顺序做什么事"的一份实战笔记。无论你是准备拿它当毕设,还是纯粹想练手SpringBoot全家桶,都可以参考。
1. 为什么家政服务平台是一个"性价比很高"的毕设选题
先聊选题。有人觉得家政服务太"土",不如电商、社交、短视频类来得有面子。但走过这个过程的人会明白,毕设选题的价值从来不在于名字炫不炫,而在于三个维度:需求是否足够清晰、模块是否能撑起工作量、答辩时是否有东西可讲。家政平台在这三个维度上表现都相当好。
家政服务的核心业务是"用户发起预约,平台派单给服务人员,服务完成后结算评价"。这个链路天然包含了最典型的业务角色——用户端(C端)、服务端(B端,也就是保洁阿姨、维修师傅)、管理端(Admin)。三个角色之间必然涉及登录鉴权、权限区分、订单流转、状态变更、消息通知、服务评价,这些全部映射到SpringBoot开发里最常考的知识点。你做完一个家政平台,相当于把SSM/SpringBoot里"用户体系+业务订单+后台管理+统计报表"四大件都过了一遍,覆盖面比单纯做个博客或问卷系统扎实太多。
另外家政平台有一个天然优势:需求来源非常生活化。你不用去编造一个奇怪的业务场景,面试官和答辩老师一看就懂,这就降低了沟通成本。你在答辩时说"用户下单预约保洁服务"和说"平台支持多维度SKU组合定价与促销活动",前者的理解门槛显然更低。对一个本科生阶段的课程设计或毕业设计而言,通俗场景+完整实现是一种更稳妥的得分策略。
而且家政平台的边界可以自由伸缩。基础版做到"用户登录、选服务、提交预约、管理员分配、服务完成"就足够毕业;往深了做,可以加支付模拟、地图选点、实时派单、消息推送、服务人员抢单、分区定价。这意味着你在一个题目下留足了扩展空间,中期检查之后想给项目"加量"也不慌。
2. 技术选型:从SpringBoot版本到数据库,每一样都得能说出理由
技术栈是毕设答辩必被问到的问题,因为这是最能看出一个学生是不是"真做了"的地方。我的推荐组合和理由如下:
2.1 核心框架:SpringBoot 2.7.x + MyBatis-Plus
SpringBoot用2.7.x是我的实际建议。不是因为最新版不好,而是两点现实原因:第一,大量现成的教程、博客、毕设源码都基于2.x版本,遇到问题搜索引擎弹药充足;第二,3.x版本在Java 17+环境下运行,如果本机恰好是JDK 8,直接跑不起来,还得折腾环境。一个毕设项目,稳定压倒折腾。
持久层我推荐MyBatis-Plus而不是纯MyBatis。MP提供了BaseMapper的通用CRUD方法,分页插件用起来也非常顺手,能把重复劳动压缩到极低。这对工期紧张的学生来说是实实在在的解放。但注意,这里有一条隐藏要求:哪怕你用了MP,也务必要会手写SQL。答辩时老师大概率会问"MyBatis-Plus和MyBatis的区别""你项目中哪些查询是手写SQL的",你要能当场解释,而不是只会说"我用了现成的框架"。
2.2 前端方案:Thymeleaf服务端渲染是一个被低估的选择
家政平台这种管理型系统,前端到底要不要用Vue?需要考虑成本。用Vue + Element UI做一个前端工程意味着技术栈变成前后端分离,意味着你要处理跨域CORS、Token传递、Vue路由懒加载等一连串问题。对于以系统功能为考核核心的毕设来说,这些属于"额外风险"。我自己最后选择了Thymeleaf模板引擎做服务端渲染:SpringBoot原生集成,页面直接写在resource/templates下,数据用Model传参,登录状态走Session,逻辑清晰,代码量小,而且整站一眼看上去就是一个结构完整的单体Web应用。
这不代表Thymeleaf没有局限性——局部刷新、异步交互不如前后端分离流畅。但应对后台管理系统的列表页、表单页、详情页完全足够。更重要的是,把精力省出来,留在后端业务和数据库设计上,那才是拿分大头。
2.3 数据库:MySQL 5.7或8.0,编码和时区是重点
数据库本身没有悬念,MySQL就对了。真正常被忽略的是两个细节:一是建库时的默认字符集,必须设置为utf8mb4而不是utf8,否则用户头像昵称或者评价里出现emoji字符时,插入会直接报错。二是连接串上的时区参数,需要在application.yml里写入serverTimezone=Asia/Shanghai,否则在部分MySQL驱动版本下会报时区错误或时间偏差8小时。
2.4 额外推荐的组件清单
光有SSM、模板引擎还不够,下面这几项几乎是我给所有做SpringBoot毕设的人的标配,实际用时也都派上了用场:
- Lombok:用@Data注解替代手写getter/setter,大幅度压缩实体类代码量。
- Hutool:工具类库,我主要用它生成订单号、处理日期字符串。
- Apache Commons Lang3:写业务逻辑时会涉及ObjectUtils.isEmpty这类判断,引入方便。
- Druid连接池:自带监控页面,答辩时打开展示一下连接池状态,有一定效果。
这些选型没有一个是猎奇或复杂的,全部是行业存量项目里最常见的东西。用面试的话来说,这叫"技术选型贴合业务复杂度",本身就是加分的点。
3. 核心功能梳理:三端角色下的模块边界与用例设计
项目里我划分了三个角色端:用户端、服务人员端、管理员端。很多做毕设的同学习惯把所有写在一个Controller里,谁调都行——这是后期维护混乱的根源。正确的做法是,从一开始就把访问边界划清楚,用不同的Controller前缀和权限校验去约束操作。
3.1 用户端(Customer):下单、支付、评价
用户端核心流程包括:
- 注册/登录(手机号+密码,JESSIONID管理Session)
- 浏览服务分类(如日常保洁、深度保洁、家电清洗、月嫂等)
- 选择具体服务项,选择服务时间,填写地址,提交预约
- 订单创建后模拟支付(这一步我使用了状态值流转,没有对接真实支付组件)
- 服务完成后对订单进行评价打分
这里有一个非常重要但新手容易漏掉的需求:用户能看到的服务项,必须是"上架"状态;用户能预约的时间,不能早于当前时间;用户下单前,需要校验余额或支付状态。这些不是想象出来的业务,而是你打开任意一个真实家政App都能观察到的规则。设计用例时多从真实产品里倒推需求,比凭空画用例图可靠得多。
3.2 服务人员端(Worker):接单、执行、回单
服务人员角色的核心是"处理被分配到自己名下的订单":
- 登录后查看分配给我的订单列表
- 查看订单详情(包含服务地址、预约时间、用户备注)
- 开始服务(状态从"待服务"变为"服务中")
- 完成服务(状态变为"待评价"或"已完成")
在设计上值得注意的一点是:服务人员不允许看到平台全量订单,只能看到经过管理员指派、且指派给自己的那一批。这就要求SQL查询中必须有worker_id这个归属条件,而不是做一个"全部订单列表"然后前端过滤。任何时候都不要把"前端隐藏数据"当成"权限控制"。
3.3 管理员端(Admin):人员、服务项目、订单调度、统计
管理端功能最多,也是整个项目工作量最好展示的地方:
- 员工账号管理(服务人员的增删改查、启用禁用)
- 服务分类和服务项目管理(上架、下架、修改价格、维护服务简介)
- 订单管理(查看所有订单、手动指派服务人员、处理订单异常、取消订单)
- 评价管理(查看用户评价,可隐藏违规评价)
- 数据统计(整体订单量、各分类订单占比、营收金额合计、服务人员接单排行)
建议数据统计部分使用ECharts图表展示。这个是整个项目视觉上最能出效果的一部分,同时在答辩演讲时能当话题延展——"数据统计维度是怎么设计的""如果数据量变大了该怎么优化",都能顺势展开。
4. 数据库设计:九张核心表的职责划分与关键字段说明
家政平台这种规模的项目,数据库表控制在9张左右最合理。过多说明你设计过度,过少又撑不起系统复杂度。我最终的表结构如下:
- member用户表(用户端):id、手机号、密码(BCrypt加密)、昵称、头像、性别、注册时间、状态
- worker服务人员表:id、姓名、手机号、密码、擅长服务分类、简介、评分(冗余统计值)、状态
- admin管理员表:id、用户名、密码、角色等级、创建时间
- category服务分类表:id、分类名称、排序值、图标、创建时间
- service服务项目表:id、分类id、服务名称、封面图、单价、计量单位、服务时长、上架状态、简介
- orders订单表:id、订单号、用户id、服务项目id、服务人员id(可空,待指派)、服务时间、地址、联系人、联系电话、订单金额、状态、创建时间、支付时间、完成时间
- evaluation评价表:id、订单id、评分、评价内容、评价图片、创建时间
- address用户地址表:id、用户id、联系人、电话、详细地址、是否为默认地址
- operate_log操作日志表:id、操作人、操作类型、操作内容、操作时间
4.1 订单状态字段怎么设计:整数+状态枚举才是正确答案
订单状态是家政系统里最核心的字段,我强烈建议用tinyint整数存储,而不是直接存中文字符串。例如:0=待支付,1=待指派,2=待服务,3=服务中,4=已完成,5=已取消,6=退款/异常。在Java代码里写一个OrderStatusEnum去映射这些数字和描述。
这样做的好处有三个:
- 数据库层面状态检索效率高,索引对数字友好;
- 前后端传递数据时不需要解析中文,不会出现"已完成"和"已完成 "这种肉眼难辨的空格差异;
- 状态机的流转条件在Java枚举里可以写死,避免业务层随意改状态。
4.2 订单号的生成规则:时间戳+随机数就够用
毕设不需要引入分布式ID方案,但直接用MySQL自增id做订单号并不专业,答辩可能会被问。我用的规则是:yyyyMMddHHmmss + 4位随机数。例如202405211430159281。这个格式可读性强,能看出下单时间,也足够支撑毕设演示场景。生成逻辑用Hutool里的DateUtil和RandomUtil拼接即可,十几行代码解决。
4.3 索引设计与冗余字段:小项目也要有意识
虽然数据量不大,但设计表时建议还是把索引加上:orders表的user_id、worker_id、status三个字段分别加普通索引,orders表的主键使用bigint自增,订单号字段加唯一索引——虽然随机数碰撞概率极低,但唯一索引能从根本上杜绝重复,也能作为一个亮点在答辩时说。另外worker表中的评价分数字段,我采用了冗余存储:每次用户提交评价时,更新该worker累计评分和评价次数,展示时直接取平均值即可,不需要每次都去重算。这种空间换时间的思路,在答辩时同样是可以讲的优化点。
5. 核心痛点与编码实现:登录鉴权、下单流程和状态机
到了代码实现这一层,我只挑三个最考验业务思维的环节展开:登录鉴权的实现方式、下单预约的完整事务流程、订单状态机的流转控制。
5.1 登录鉴权:Session方案 + 拦截器校验权限
Thymeleaf单体应用不需要引入Spring Security或JWT,用Session配合HandlerInterceptor就能把权限控制做得很清晰。
我写了两个拦截器:
- LoginInterceptor:检查当前Session中是否存在"loginUser"/"loginWorker"/"loginAdmin",不存在就重定向到对应用户端或后台的登录页。
- AdminInterceptor:在LoginInterceptor的逻辑基础上,再校验Session中的角色值是否等于管理员,防止普通用户登录后直接访问admin路径下的管理接口。
注册拦截器时要注意SpringBoot 2.x的写法:实现WebMvcConfigurer接口,重写addInterceptors方法,设置excludePathPatterns排除登录页、静态资源(/static/**)、用户注册接口等。如果你在org.springframework.boot:spring-boot-starter-web里直接继承WebMvcConfigurerAdapter,那是旧版写法,现在编译会报错。
5.2 下单预约的核心事务与校验顺序
下单是整个系统里逻辑最复杂的操作,涉及多张表的数据变化。我的下单方法事务实现顺序如下:
- 校验用户登录状态,取出当前登录用户id。
- 根据serviceId查询服务项目,检查服务是否处于上架状态。
- 校验提交的serviceTime不能早于当前时间(精确到分钟)。
- 校验联系人、联系地址不为空。
- 生成订单号,构造Order实体,状态设置为0(待支付)。
- 插入订单记录,返回订单id。
- 用户点击"模拟支付",在支付方法中根据订单id将订单状态从0更新为1(待指派),同时更新支付时间。
这里有一个值得专门说的问题:下单和支付要不要放在同一个方法里?我的选择是拆开,因为现实业务中"创建订单"和"支付"本来就是两个动作。拆开之后,订单数据能保留未支付的状态,列表里可以展示"待支付"标签,逻辑更干净。如果你把两个动作硬拼在一个方法,出现支付失败、订单却创建成功的场景就非常难处理。
在事务管理上,下单方法标注@Transactional(rollbackFor = Exception.class),表示任何异常都回滚。很多同学只写@Transactional不写rollbackFor,这是不严谨的。默认情况下RuntimeException才触发回滚,如果方法里出现受检异常而你没有声明回滚类型,数据可能就"半成功"地写入库了。这一段我建议在答辩时主动说出来,能看出你是真研究过。
5.3 状态机的控制:集中写一个执行状态变更的服务
订单状态流转如果散在多个Service里到处写update语句,后面改需求时会焦头烂额。我专门写了一个OrderStatusService,方法签名类似:
public boolean changeOrderStatus(Long orderId, Integer fromStatus, Integer toStatus)所有涉及订单状态变化的地方,都直接调用这个方法。底层SQL是:
UPDATE orders SET status = #{toStatus}, update_time = NOW() WHERE id = #{orderId} AND status = #{fromStatus}这里用上了"条件更新"的巧思:如果订单当前状态不是预期的fromStatus,更新影响行数为0,方法返回false,说明本次状态流转非法。这样从数据库层面就杜绝了用户反复提交、并发操作导致的状态错乱。
举例来说:用户支付订单,fromStatus传0、toStatus传1;管理员指派服务人员,fromStatus传1、toStatus传2;服务人员开始服务,fromStatus传2、toStatus传3。整个链路清晰,而且每一条流转都有审计可查。
5.4 管理员指派服务人员时的分页和条件查询
指派功能的后端核心是:查询当前状态为"待指派"的订单列表,并为每一笔订单选择可用的服务人员。这里我用MyBatis-Plus的分页插件,配合自定义查询条件。SQL里需要JOIN三张表:orders、service、worker。查询条件包括订单号模糊查询、下单时间范围查询、状态筛选。直接用LambdaQueryWrapper处理单表简单查询,遇到多表查询就在Mapper里写@Select注解的SQL语句。一个Mapper方法一个SQL,结构保持简单明了。
6. 真正的坑都在这里:版本兼容、路径映射和前端联调
讲完了主干实现,再说项目落地过程中最容易让人卡壳的几个坑。很多同学代码逻辑写得很顺,一运行就是404、500轮着报,十有八九都是下面这些问题。
6.1 SpringBoot版本过高导致的javax→jakarta包名问题
这是在SpringBoot 3.x时代非常高频的报错。如果你用了Spring Boot 3.x,会发现自己import javax.servlet.http.HttpServletRequest时编译不过去。原因是从Spring Boot 3.0开始,Servlet规范从javax迁移到了jakarta。如果按照老教程写拦截器、Controller,看到红色报错不要慌,把javax改成jakarta即可。但我个人建议毕设还是用2.7.x,避掉这层折腾。
6.2 静态资源404:Thymeleaf模板路径和静态资源路径的问题
新手最常见的错误是:application.yml里没配置spring.mvc.static-path-pattern或配置成了/static/**,然后页面里的css、js全部加载不出来。默认情况下SpringBoot的静态资源是映射到/**的,实际放在classpath:/static/目录下即可。如果你自定义了拦截器,还要注意excludePathPatterns里有没有放行静态资源路径,否则登录页面会拿不到bootstrap样式。
6.3 使用MyBatis-Plus时时间字段自动填充
很多人在插入订单时手动setCreateTime,但更省事的做法是使用MP的自动填充功能。在实体类的createTime字段上标注@TableField(fill = FieldFill.INSERT),然后写一个MetaObjectHandler实现类,在insertFill方法里统一设置创建时间。这里有一个坑:一旦引入了MP的自动填充,你在别处手写insert操作时有可能因为漏掉时间字段,插入的数据时间字段为空。我最后所有订单写入操作都在同一个地方维护,避免了这个问题的扩散。
6.4 前端表单提交时的日期格式问题
Thymeleaf页面里提交datetime-local类型的 ,浏览器传给后端的时间字符串会是"2024-05-21T14:30"这种带T的格式。如果后端直接用Date类型接收并配置了指定pattern,就会报格式转换异常。我的解决办法是在实体类上增加@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")注解,或者在Controller入参前用字符串接住,再通过Hutool的DateUtil.parse转换为Date类型。推荐后者,因为后端前后格式可控,不被框架的全局配置牵连。
6.5 时间显示相差8小时
这个问题通常只出现在展示层。如果MySQL连接串没设置serverTimezone参数,JDBC驱动(可能是高版本)默认使用服务器本地时区,而服务器时区可能是UTC,就会导致查询出的时间比实际早8小时。排查方法很简单:在application.yml的数据库连接串后增加serverTimezone=Asia/Shanghai,并且实体类中时间字段统一使用java.util.Date或LocalDateTime,尽量避免混用。
7. 部署测试与答辩准备:让项目跑在别人的机器上也不慌
项目写完不算完,你需要在至少一台"全新环境"的机器上把它跑起来,然后以答辩评委的身份去审视它。很多同学在自己电脑上开发时依赖了一堆本机专用配置,换一台机器就起不来,这类情况必须提前规避。
7.1 打包部署:用Maven打jar包而非在IDE里跑
SpringBoot项目推荐打成可执行jar包部署,这样在服务器上有Java环境就能运行。项目根目录执行:
mvn clean package -Dmaven.test.skip=true打包完成后,后台启动方式:
nohup java -jar target/housekeeping-platform-0.0.1-SNAPSHOT.jar --server.port=8080 > app.log 2>&1 &这里有一个常踩的坑:如果你在application.yml中配置了文件上传路径或本地磁盘路径,换机器后这些绝对路径可能不存在。我统一把这些路径配置放在application.yml里并通过@ConfigurationProperties注入,部署时只需修改配置文件,不需要改代码。这也是一个可以在答辩时提及的"工程化设计"。
7.2 测试用例的准备:比数量更重要的是覆盖关键业务链路
毕设演示最忌讳的是"当场操作不顺"。我的建议不是要你写几百个单元测试,而是至少把手动冒烟用例列成一张表,按顺序执行一遍,确认没有问题。我的核心用例表至少包含:
- 用户注册→登录→浏览服务分类→查看服务详情→下单→支付
- 管理员登录→添加服务人员→新增服务分类→新增服务项目→查看订单→指派服务人员
- 服务人员登录→查看我的订单→开始服务→完成服务
- 用户对已完成的订单提交评价→管理员后台查看评价
- 非法访问:用户直接访问admin路径被拦截跳转
这几条链路完整走通后,演示过程中基本不会翻车。如果有条件,建议录屏存一份,防止演示现场领导电脑连不上数据库。
7.3 答辩时老师常问的几个问题,提前准备答案
结合我实际参加答辩和帮助别人答辩的经验,老师对这个项目偏好问以下几个问题:
- 订单状态是怎么管理的?如何在并发下防止状态错乱? 答:状态用整数枚举表示,状态流转变更走专门的Service方法,底层是带状态条件的UPDATE,影响行数为0即非法流转。
- MyBatis-Plus和MyBatis的区别?你手写了哪些SQL? 答:MP内置通用CRUD、分页插件、自动填充;手写SQL集中在多表关联查询和统计类SQL,并解释一两个具体场景。
- 如何保证用户只能操作自己的订单? 答:所有订单查询方法初始化时都强制带上user_id条件,用户id从Session中获取而非前端传参;前端传参可被篡改是新手最易忽视的漏洞。
- 如果数据量大了,系统怎么优化? 答:从索引优化、分库分表思路、缓存(Redis缓存服务项目和热门分类)、读写分离(引入主从)这几个方向作答。不需要真的实现,但要有思路。
- 为什么要选这个题目?创新点在哪? 答:回答的要点是业务链条完整、角色清晰、贴近真实的O2O业务形态,以及你为了提升体验做的细节优化,比如订单状态机、条件更新防止并发错乱、操作日志审计。创新点可以体现在方案设计的严谨性,不必硬凹一个高深算法。
8. 写在最后的心得
回头再看这个项目,家政服务平台的复杂度恰好落在"一个本科生跳一跳能够到"的位置上。它需要你认真设计数据库表结构,需要你理解订单状态在真实业务中的流转,也需要你面对前端页面和后端Controller互相配合的那些琐碎问题。做完这一整套,你对SpringBoot的理解不会再停留在"照着教程写个HelloController"的层面。
如果你正在准备这个题目,我最后有一个建议:一定不要只做"能跑",而是要为项目写一份结构清晰的README,把启动步骤、默认账号、测试数据、核心流程说明放进去。这一份文档既是答辩的底气,也是你代码之外的加分项。遇到问题也不要急着搜"毕设源码"往下抄,自己把Bug调通一遍,远比拿到一份完美源码更有收获。祝顺利。