
简介一套基于Spring Boot的家政服务管理平台毕业设计项目源码包面向Java方向毕业生和课程设计学生也适合正在学习Spring Boot的开发者参考。系统同时包含前台操作与后台管理两条业务线用户端覆盖首页、服务信息、公告信息、留言反馈、个人中心等常见功能管理端则提供用户、服务人员、服务类型、服务信息、预约、取消、分配、进度、评价、留言反馈、系统管理等一系列完整模块流程连贯且贴近真实项目可直接作为毕设或课设的代码基础与讲解框架。压缩包约75.21MB内含Spring Boot项目源码、LW设计文档、PPT演示文件和配套演示视频项目已配置好可正常启动的运行环境开发语言为Java采用JDK1.8、Tomcat7、MySQL5.7和Maven3.3.9并支持在Eclipse、MyEclipse、IDEA等常见IDE中打开运行。目前已有313人学习浏览利用这份资源可以快速梳理项目结构、理解权限管理和业务流转还能直接修改复用节省搭建时间提升毕设完成质量。1. 家政服务管理平台为什么值得用 Spring Boot 做毕业设计家政服务管理的核心难点不在页面多而在角色杂、状态乱雇主下单、平台派单、服务人员上门、双方评价中间还有取消、改期、投诉。用 Spring Boot 做这类平台等于把一套完整的 Web 后端工程能力压缩到一个可演示的系统里。对准备 springboot 毕设的人来说家政服务管理平台能覆盖用户管理、服务分类、订单状态流转、评价结算和权限控制这些高频出题点而且找源码参考时容易对照数据库字段验证。下面按“建模→编码→排错→演示”的顺序讲清楚一个能本地跑通的 springboot 家政服务管理平台是怎么搭起来的。2. Spring Boot 家政服务管理平台的技术栈选型与数据模型2.1 技术选型Spring Boot 2.7 MyBatis Plus 3.5 的搭配理由毕业设计场景里技术选型的第一原则是“能讲清楚原理且遇到问题查得到资料”。Spring Boot 2.7.x 是目前毕设和存量项目里使用最广的版本线相比 3.x它不需要迁移 jakarta 命名空间跟 MyBatis Plus、Shiro 这类老牌组件的兼容性文档也齐全。搭配 MyBatis Plus 3.5.x 的理由更实际单表 CRUD 不用手写 SQL分页、逻辑删除、自动填充在答辩时可以直接归为“框架能力”不用贴一屏代码去解释。项目里常见的依赖组合如下dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.30/version /dependencySpring Boot 的自动配置会在 classpath 里发现这两个 starter 后自动装配数据源和 SqlSessionFactory。需要改的是 application.yml 里的 datasource 与 mybatis-plus 两段配置spring: datasource: url: jdbc:mysql://localhost:3306/housekeeping_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0 configuration: map-underscore-to-camel-case: true这里 serverTimezone 必须显式指定否则 JDBC 驱动 8.x 会拿系统默认时区去拼接Windows 中文环境下经常报时区异常。map-underscore-to-camel-case开启后数据库的 create_time 字段可以直接映射到实体的 createTime 属性省去大量 ResultMap 配置。逻辑删除的配置值得单独提一句家政平台的服务项、服务人员都可能做下线而非物理删除配置了全局逻辑删除后MyBatis Plus 内置的 selectById、updateById 会自动过滤 deleted1 的数据不需要每个 Mapper 里重复写 WHERE 条件。这在答辩时是一个可以主动讲出来的设计点。2.2 核心数据表设计把业务对象转成表结构家政服务管理平台的数据实体要覆盖三端管理员的系统配置、雇主的找服务流程、服务人员的履约流程。最少需要这几张表表名关键字段说明sys_userid, username, password, role, nicknamerole 区分 admin / customer / workerservice_categoryid, name, sort保洁、月嫂、保姆、维修等一级分类service_itemid, category_id, name, price, unit, tags具体服务项tags 存技能标签customer_orderid, order_no, customer_id, worker_id, item_id, status, appoint_time, address, remark订单主表status 是状态机字段order_commentid, order_id, customer_id, worker_id, score, content双边评价表worker_profileid, user_id, years_of_exp, score, audit_status服务人员档案关联 sys_user实际建表时要注意order 是 MySQL 保留字所以表名用 customer_order 更稳妥。order_no 建议用业务编号而不是自增 id格式类似JK20250501001对外展示用业务单号内部关联用主键这在答辩时可以说明是“对外隔离主键的一种常规做法”。service_item 的 tags 字段需要做一个取舍。字符串存逗号分隔例如“油烟机清洗,深度保洁,高温消毒”查询时用 MyBatis Plus 的 like 匹配几千条数据量下完全没问题如果要做严格的技能匹配再拆一张 worker_skill 关联表。毕设场景推荐前者代码量少且演示效果好。一个核心的建表语句如下CREATE TABLE customer_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 业务单号, customer_id BIGINT NOT NULL COMMENT 雇主id, worker_id BIGINT DEFAULT NULL COMMENT 服务人员id, item_id BIGINT NOT NULL COMMENT 服务项id, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0待接单 1已接单 2服务中 3待评价 4已完成 5已取消, appoint_time DATETIME COMMENT 预约上门时间, address VARCHAR(255) COMMENT 服务地址, remark VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted TINYINT DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;状态字段用 TINYINT 而不是字符串枚举是为了排序和索引效率具体含义通过代码里的常量或枚举类控制而不是直接散落在业务逻辑里。状态流转的约束放到 Service 层统一管理后面详述。2.3 分层代码的最小单元从 Controller 到 Mapper 的完整链路搭好表之后按一个纵向用例把分层跑通。拿“查询某个分类下的所有服务项”来说Controller 层RestController RequestMapping(/api/item) public class ServiceItemController { Resource private ServiceItemService serviceItemService; GetMapping(/list) public Result list(RequestParam Long categoryId, RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize) { PageServiceItem page serviceItemService.queryByCategory(categoryId, pageNum, pageSize); return Result.ok(page); } }Service 层Service public class ServiceItemServiceImpl extends ServiceImplServiceItemMapper, ServiceItem implements ServiceItemService { Override public PageServiceItem queryByCategory(Long categoryId, Integer pageNum, Integer pageSize) { return lambdaQuery() .eq(ServiceItem::getCategoryId, categoryId) .eq(ServiceItem::getStatus, 1) .orderByAsc(ServiceItem::getSort) .page(new Page(pageNum, pageSize)); } }这段代码里lambdaQuery()是 MyBatis Plus 3.x 提供的条件构造器用方法引用代替字符串列名编译期就能发现字段名拼写错误。eq(ServiceItem::getCategoryId, categoryId)生成WHERE category_id ?条件。page(new Page(pageNum, pageSize))会自动拼接 LIMIT 并返回分页对象前端拿到的数据结构里包含 records、total、pages 字段。到这里一个最小的“请求 → 条件构造 → SQL → JSON 响应”链路已经闭合。理解这条链路后后续所有模块都只是在 Service 里增加不同的业务逻辑这也是很多 springboot 项目源码里代码结构高度一致的原因。3. 家政服务管理平台的三个核心业务模块实现3.1 服务分类与技能标签从产品需求到可检索字段平台首页的第一屏永远是分类导航。家政平台的产品需求一般是一级分类固定展示保洁、月嫂、保姆、搬家、维修点进去看到服务项列表服务项支持价格倒序、按标签过滤。用前面设计好的表分类展示几乎零成本一个 category 表一个 item 表联查只用 item.category_id 关联。真正的业务量在“技能标签过滤”这个查询上。比如“能接油烟机清洗且已上架的服务项”一个常见写法是LambdaQueryWrapperServiceItem wrapper new LambdaQueryWrapper(); wrapper.like(ServiceItem::getTags, 油烟机清洗) .eq(ServiceItem::getStatus, 1);like参数会生成%油烟机清洗%如果后续数据量变大可以换全文索引或右匹配油烟机清洗%。毕设数据量下这是最优解——不要在字符串标签上建关联表徒增 join 复杂度。这个模块展示时能讲的话术是分类解决路径导航标签解决同分类内的细分。两者语义不同所以分开建模。3.2 预约下单与订单状态机状态流转是毕设的重头戏订单模块是家政服务管理平台最核心的演示点也是最容易在代码里写乱的地方。常见错误是直接在 Controller 里写if (status 0)的散装判断状态多了以后改一个逻辑要翻好几个方法。简化掉支付通知后订单的合法状态变化如下0 待接单 → 1 已接单用户取消则 → 5 已取消 1 已接单 → 2 服务中接单后可取消需走协商 2 服务中 → 3 待评价服务完成 3 待评价 → 4 已完成双方评价后在 Service 层里用一个枚举加一张状态迁移表把规则集中管理public enum OrderStatus { WAITING(0), ACCEPTED(1), SERVING(2), REVIEWING(3), DONE(4), CANCELLED(5); final int code; OrderStatus(int code) { this.code code; } }private static final MapInteger, SetInteger ALLOWED_TRANSITIONS Map.of( 0, Set.of(1, 5), 1, Set.of(2, 5), 2, Set.of(3), 3, Set.of(4) );下单接口的核心逻辑Transactional public Long createOrder(OrderCreateDTO dto) { CustomerOrder order new CustomerOrder(); order.setOrderNo(generateOrderNo()); order.setCustomerId(dto.getCustomerId()); order.setItemId(dto.getItemId()); order.setStatus(OrderStatus.WAITING.code); order.setAppointTime(dto.getAppointTime()); order.setAddress(dto.getAddress()); save(order); return order.getId(); }generateOrderNo()生成业务单号可以用yyyyMMddHHmmss 4位随机数也可以先查当天已有订单数后自增。后一种更严谨但多一次查询毕设用前一种足够答辩时被追问再补一句“生产环境会换 Redis INCR 保证递增”。状态变更统一走一个方法Transactional public void changeStatus(Long orderId, int fromStatus, int toStatus) { CustomerOrder order getById(orderId); if (order null) { throw new BizException(订单不存在); } SetInteger allowed ALLOWED_TRANSITIONS.get(fromStatus); if (allowed null || !allowed.contains(toStatus)) { throw new BizException(非法状态流转); } order.setStatus(toStatus); updateById(order); }这个方法在答辩演示时的价值是直接体现在报错上连续点击“完成订单”按钮第二次会抛“非法状态流转”而不是像很多 CRUD 项目那样把 status 原样覆盖一遍。3.3 评价与结算双边评价的闭环设计家政平台与普通电商的差别在于服务完成后是双边评价雇主评价服务人员的技能和态度服务人员也可以评价雇主比如是否好沟通、家里是否方便作业。这个闭环设计能体现完整的业务思考。评价表按前面的设计order_id 加唯一约束保证一单一评。核心接口如下PostMapping(/submit) public Result submitComment(RequestBody CommentDTO dto) { // 校验订单状态必须是待评价 CustomerOrder order orderService.getById(dto.getOrderId()); if (order.getStatus() ! OrderStatus.REVIEWING.code) { throw new BizException(当前订单状态不可评价); } OrderComment comment new OrderComment(); comment.setOrderId(dto.getOrderId()); comment.setCustomerId(dto.getCustomerId()); comment.setWorkerId(dto.getWorkerId()); comment.setScore(dto.getScore()); comment.setContent(dto.getContent()); comment.setRole(dto.getRole()); // 1 雇主评服务人员2 服务人员评雇主 commentService.save(comment); // 双方都评价后订单置为完成 if (commentService.isBothCommented(dto.getOrderId())) { orderService.changeStatus(dto.getOrderId(), OrderStatus.REVIEWING.code, OrderStatus.DONE.code); } return Result.ok(); }isBothCommented的实现是在 order_id 维度按 role 字段统计评价条数等于 2 说明双边评价完成。这里用一张表存双边评价比拆两张表省事数据模型上也清晰评价是“挂在订单上的事件”不是“挂在人身上的属性”。同步更新服务人员的平均分是一个加分项Double avgScore commentService.lambdaQuery() .eq(OrderComment::getWorkerId, dto.getWorkerId()) .eq(OrderComment::getRole, 1) .select(OrderComment::getScore) .list() .stream() .mapToInt(OrderComment::getScore) .average() .orElse(0); workerProfileService.lambdaUpdate() .eq(WorkerProfile::getUserId, dto.getWorkerId()) .set(WorkerProfile::getScore, BigDecimal.valueOf(avgScore).setScale(1, RoundingMode.HALF_UP)) .update();两个操作放进同一个事务里避免出现评价已落库但评分没更新的中间态。这个细节在答辩时很加分直接体现事务意识。4. 权限、并发与通知Spring Boot 家政平台进阶点的落地4.1 基于 JWT 的登录鉴权与三端角色控制家政管理平台天然是三类角色共用一个登录入口管理员、雇主、服务人员。最常见的做法是在同一个 sys_user 表里用 role 字段区分配合 JWT 做无状态鉴权用拦截器实现而不用 Spring Security是因为毕设场景下拦截器能把原理讲得更透明。JWT 工具类核心是生成和解析private static final SecretKey KEY Keys.hmacShaKeyFor( housekeeping-platform-secret-key-2025.getBytes() ); public static String createToken(SysUser user) { return Jwts.builder() .setSubject(user.getId().toString()) .claim(role, user.getRole()) .claim(username, user.getUsername()) .setExpiration(new Date(System.currentTimeMillis() 24 * 3600 * 1000)) .signWith(KEY, SignatureAlgorithm.HS256) .compact(); }生成 token 时把 role 放在 claim 里拦截器解析后可以直接拿角色做权限判断不用再查一次数据库。24 小时过期时间对毕设演示足够答辩时可以说实际生产会换成短 token 加 refresh token。拦截器里校验的关键代码Component 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.isBlank()) { throw new BizException(未登录); } try { Claims claims Jwts.parserBuilder() .setSigningKey(JwtUtil.getKey()) .build() .parseClaimsJws(token.replace(Bearer , )) .getBody(); request.setAttribute(userId, Long.valueOf(claims.getSubject())); request.setAttribute(role, claims.get(role)); } catch (JwtException e) { throw new BizException(token无效或过期); } return true; } }注册拦截器时注意排除登录接口和静态资源Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/login, /api/register, /api/auth/**); }这里的踩坑点在于如果前端把上传图片放在 static 下的 /upload/**且没做静态资源映射拦截器会把资源请求也拦掉。实际排错时先看请求路径是否落进了 addPathPatterns 的匹配范围。角色校验在方法内判断即可String role (String) request.getAttribute(role); if (!admin.equals(role)) { throw new BizException(无权限操作); }对 springboot 项目源码做二次开发时这个拦截器类通常是第一个要看的地方——它决定了所有接口的进入门槛。4.2 订单并发提交的防重与幂等控制答辩时一个高频追问是“用户连续点击两次提交订单按钮会不会生成两条订单”标准答案分两层数据库层给 order_no 加唯一索引兜底接口层用 Redis 的 setIfAbsent 做防重。Boolean first redisTemplate.opsForValue() .setIfAbsent(order:create: dto.getRequestId(), 1, Duration.ofSeconds(10)); if (Boolean.FALSE.equals(first)) { throw new BizException(请勿重复提交); }这个方案的关键在于 requestId 由前端在打开下单页时生成并随请求携带。setIfAbsent成功说明这是第一次提交10 秒内重复提交会被拒绝。没有 Redis 的工程可以用内存 ConcurrentHashMap 加过期时间做简易替代但演示效果远不如 Redis 方案。如果项目里已集成 Redis 做缓存这个防重操作没有额外成本只多一条 key。4.3 定时任务实现超时自动取消与订单通知家政平台有一条自然业务规则“用户下单后 30 分钟没有服务人员接单系统自动取消”。用 Spring 自带的 Scheduled 就能实现Component public class OrderAutoCancelTask { Resource private CustomerOrderMapper orderMapper; Scheduled(cron 0 */5 * * * ?) public void autoCancelExpiredOrders() { ListCustomerOrder expiredOrders orderMapper.selectExpiredWaiting( LocalDateTime.now().minusMinutes(30)); for (CustomerOrder order : expiredOrders) { orderMapper.updateStatus(order.getId(), OrderStatus.CANCELLED.code); } } }配套的 Mapper 里用注解 SQLSelect(SELECT * FROM customer_order WHERE status 0 AND create_time #{expireTime} LIMIT 100) ListCustomerOrder selectExpiredWaiting(LocalDateTime expireTime);启动类上记得加EnableScheduling否则 Scheduled 不生效。这里的缺陷是每个定时周期只取 100 条避免一次更新太多行锁住 InnoDB 的区间索引。自动取消后往 notice 表插一条消息用户登录后在消息中心能看到“订单超时已取消”。0 */5 * * * ?里的?是 Quartz 风格的“不指定域”Spring 5.3 之后兼容。如果改用Scheduled(fixedDelay 300000)含义是上一次任务执行完再等 300 秒跑下一次不会出现任务堆积cron 则按自然时间触发若任务执行时间超过周期可能造成重叠执行需要自行加分布式锁或同步块。对毕设来说固定周期更稳妥。5. 从 springboot 项目源码到本地跑通与答辩演示5.1 15 分钟启动工程的配置清单拿到一个 springboot 毕设源码后第一步永远是看数据库脚本和 application.yml而不是直接点启动。通常的操作顺序是mysql -uroot -p CREATE DATABASE housekeeping_db DEFAULT CHARSET utf8mb4; exit; mysql -uroot -p housekeeping_db sql/housekeeping.sql mvn spring-boot:run启动失败时按下面顺序排查本地 MySQL 版本是否为 5.7 以上application.yml 里的 database 名称和账号密码是否与本地一致pom.xml 里的 spring-boot-starter-parent 版本是否和当前 JDK 匹配。Spring Boot 2.7 用 JDK 8 或 11 最稳JDK 17 能跑但部分第三方 starter 版本不够新时会报模块访问错误。5.2 答辩演示的 5 条必走路径家政平台的演示不要按代码顺序走按业务叙事走管理员登录创建两个服务人员账号并审核——展示用户管理与审核流程。雇主登录按分类浏览服务项提交预约订单——展示分类检索和下单。服务人员登录在待接单列表点击接单——展示订单状态从 0 到 1。雇主确认服务开始、结束进入评价页提交评价——展示状态到 3 再到 4。切到管理员视图查看订单列表和统计报表。先跑通这条路径再讲代码把每一步数据在表里的变化讲清楚比对着 CRUD 念代码更有说服力。5.3 三个高频排错点与一个交付技巧本地跑 springboot 家政平台的高频报错按概率排序现象原因解法数据库连接报时区错误驱动 8.x 要求显式指定时区url 加serverTimezoneAsia/Shanghai登录后页面 404拦截器没放行静态资源检查 WebMvcConfigurer 的 addResourceHandlersRedis 相关报错项目集成了 Redis 但本地没启动启动 redis-server或注释相关配置改内存模式第三个问题在毕设源码里最常见——很多项目带 Redis 做缓存或幂等演示机没装 Redis 直接启动失败。答辩前把 redis-server 跑起来能省掉现场一半的风险。交付层面的一个技巧给项目补一个application-local.yml把端口、数据库、Redis 地址写死为 localhost启动时用--spring.profiles.activelocal指定。这样任何人拿到源码都能最小改动跑起来可交付性比只丢一个 zip 包要好得多也能体现对 springboot 多环境配置的熟练度。本文还有配套的精品资源点击获取