news 2026/9/11 18:54:02

基于Spring Boot的家政服务管理平台:从数据建模到订单状态机完整实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Spring Boot的家政服务管理平台:从数据建模到订单状态机完整实现

简介:一套基于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> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency>

Spring Boot 的自动配置会在 classpath 里发现这两个 starter 后,自动装配数据源和 SqlSessionFactory。需要改的是 application.yml 里的 datasource 与 mybatis-plus 两段配置:

spring: datasource: url: jdbc:mysql://localhost:3306/housekeeping_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/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 会自动过滤 deleted=1 的数据,不需要每个 Mapper 里重复写 WHERE 条件。这在答辩时是一个可以主动讲出来的设计点。

2.2 核心数据表设计:把业务对象转成表结构

家政服务管理平台的数据实体要覆盖三端:管理员的系统配置、雇主的找服务流程、服务人员的履约流程。最少需要这几张表:

表名关键字段说明
sys_userid, username, password, role, nicknamerole 区分 admin / customer / worker
service_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 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

状态字段用 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) { Page<ServiceItem> page = serviceItemService.queryByCategory(categoryId, pageNum, pageSize); return Result.ok(page); } }

Service 层:

@Service public class ServiceItemServiceImpl extends ServiceImpl<ServiceItemMapper, ServiceItem> implements ServiceItemService { @Override public Page<ServiceItem> 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 关联。

真正的业务量在“技能标签过滤”这个查询上。比如“能接油烟机清洗且已上架的服务项”,一个常见写法是:

LambdaQueryWrapper<ServiceItem> 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 Map<Integer, Set<Integer>> 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("订单不存在"); } Set<Integer> 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() { List<CustomerOrder> expiredOrders = orderMapper.selectExpiredWaiting( LocalDateTime.now().minusMinutes(30)); for (CustomerOrder order : expiredOrders) { orderMapper.updateStatus(order.getId(), OrderStatus.CANCELLED.code); } } }

配套的 Mapper 里用注解 SQL:

@Select("SELECT * FROM customer_order WHERE status = 0 AND create_time < #{expireTime} LIMIT 100") List<CustomerOrder> 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 条必走路径

家政平台的演示不要按代码顺序走,按业务叙事走:

  1. 管理员登录,创建两个服务人员账号并审核——展示用户管理与审核流程。
  2. 雇主登录,按分类浏览服务项,提交预约订单——展示分类检索和下单。
  3. 服务人员登录,在待接单列表点击接单——展示订单状态从 0 到 1。
  4. 雇主确认服务开始、结束,进入评价页提交评价——展示状态到 3 再到 4。
  5. 切到管理员视图,查看订单列表和统计报表。

先跑通这条路径再讲代码,把每一步数据在表里的变化讲清楚,比对着 CRUD 念代码更有说服力。

5.3 三个高频排错点与一个交付技巧

本地跑 springboot 家政平台的高频报错按概率排序:

现象原因解法
数据库连接报时区错误驱动 8.x 要求显式指定时区url 加serverTimezone=Asia/Shanghai
登录后页面 404拦截器没放行静态资源检查 WebMvcConfigurer 的 addResourceHandlers
Redis 相关报错项目集成了 Redis 但本地没启动启动 redis-server,或注释相关配置改内存模式

第三个问题在毕设源码里最常见——很多项目带 Redis 做缓存或幂等,演示机没装 Redis 直接启动失败。答辩前把 redis-server 跑起来能省掉现场一半的风险。

交付层面的一个技巧:给项目补一个application-local.yml,把端口、数据库、Redis 地址写死为 localhost,启动时用--spring.profiles.active=local指定。这样任何人拿到源码都能最小改动跑起来,可交付性比只丢一个 zip 包要好得多,也能体现对 springboot 多环境配置的熟练度。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 18:53:13

Dota 2玩家熟练度二分类实战 从对局统计到AUC建模方案

这道 Kaggle 案例聚焦于一个很有代表性的行为识别问题&#xff1a;依据单场 Dota 2 对局统计&#xff0c;判断玩家是否属于熟练玩家。场景来自电竞&#xff0c;但建模本质并不是游戏研究&#xff0c;而是从高噪声、强随机性的行为数据中提取稳定的能力信号。 文章内容围绕结构…

作者头像 李华
网站建设 2026/9/11 18:51:44

北京GEO优化公司怎么选:预算、服务商与风险核验

随着生成式AI搜索持续渗透&#xff0c;GEO已经成为北京企业构建品牌认知和获取精准流量的重要工作。北京服务商数量多、路线差异大&#xff0c;中小微企业常见问题是看不懂技术、容易被低价吸引、又担心大牌方案溢价过高。高性价比的判断&#xff0c;应回到投入产出&#xff0c…

作者头像 李华
网站建设 2026/9/11 18:51:08

YOLO车辆检测数据集清洗与三类别训练实战指南

简介&#xff1a;本资源是面向计算机视觉初学者与YOLO模型实践者的车辆检测专用数据集&#xff0c;专为训练多类别目标检测模型设计&#xff0c;适用于自动驾驶感知模块开发、智能交通监控系统搭建等实际场景。数据集共5380个文件&#xff0c;包含1793张高质量JPG车辆图像&…

作者头像 李华
网站建设 2026/9/11 18:50:29

利用队列分支限界法求解0/1背包问题c++源码

分支限界法求解单源最短路径.zip作为一种在搜索树里寻觅最优解的算法, 分支限界法常常被用于处理像旅行商问题、0-1背包问题这类最优化问题。在本案例当中, 我们所留意的是怎样借助分支限界法去求解得到单源最短路径问题。C代码解决0-1背包问题&#xff08;分支限界法&#xff…

作者头像 李华
网站建设 2026/9/11 18:49:06

Java线程顺序控制:join、CountDownLatch与CompletableFuture实战

1. 线程顺序控制的本质与挑战在Java并发编程中&#xff0c;线程顺序控制是一个看似简单却暗藏玄机的话题。想象一下这样的场景&#xff1a;你正在开发一个电商订单系统&#xff0c;需要先调用库存服务检查库存&#xff0c;然后调用支付服务处理付款&#xff0c;最后调用物流服务…

作者头像 李华