Java家政服务平台实战:Spring Boot+MySQL订单状态设计
简介这是一份基于Java的家政服务平台毕业设计论文以Word文档形式呈现适合计算机相关专业学生、Java开发者及需要完成类似课题的研究者参考。资源内容完整包含摘要、目录、绪论、系统分析、设计与实现等结构围绕Spring Boot框架和MySQL数据库展开系统设计了管理员、雇主、雇员三个核心角色覆盖个人中心、服务项目管理、需求信息管理、预约申请、合同签订、评价反馈等关键业务模块。文档同时探讨了界面布局的易用性和数据安全方案为同类平台开发提供了可借鉴的架构思路。资源包共1个docx文件大小1.07MB直接下载即可查看全部论文内容。目前已有49人学习下载适合用于毕业设计选题、开题报告撰写、系统功能梳理或开发实战参考。整体内容结构清晰从研究背景到技术选型再到具体实现均有论述能够帮助读者快速理解家政服务平台的完整设计与实现逻辑。1. 基于 Java 的家政服务平台是什么一个能写进简历的完整业务闭环第一眼看到「基于java的家政服务平台的设计和实现.docx」这种题目很多人直接就打开 Word 开始堆需求。但按我做过的课程设计和毕业设计辅导经验家政服务平台其实是 Java Web 里回报率最高的题目之一它不只是一堆增删改查而是把用户、服务人员、订单、评价、支付串成一条带状态约束的业务链。把这条链走通你就同时练到了 Java 集合、JWT 登录、MySQL 事务、接口设计和并发排错这比单独刷招聘网站上那些 java 面试八股文更能说明问题。这篇笔记的目标很简单不看别人模板把选型、数据库、核心代码和常见坑一次说清。2. Spring Boot MySQL 的选型理由与项目骨架先让服务跑起来2.1 为什么不是 SSH 也不是 SSM而是 Spring Boot MySQL写家政服务平台第一关是技术选型。如果你还在用 Struts2 Hibernate 那套 SSH直接劝退Hibernate 的懒加载和对象关系映射在订单这种多状态业务里很容易变成黑匣子查出来的对象突然抛 LazyInitializationException排查半天。传统 SSMSpring MVC Spring MyBatis也能做但要手动配数据源、事务管理器、JSON 转换器光配置文件就够写一上午进度全耗在环境上。家政平台的典型业务是订单预约和状态流转这需要你精确控制 SQL。MyBatis 半自动映射能让你把「查询有空闲服务人员的时间段」这种带时间比较的 SQL 直接写在注解或 XML 里比 Hibernate 拼 Criteria 直观得多。Spring Boot 则把 Tomcat 嵌入、自动配置、健康检查这些东西全部收编你只需关注 application.yml 里几个参数。实际学生项目里Spring Boot 2.7 MyBatis MySQL 5.7/8.0 是当前最可靠的组合网上 java 课程设计案例源码大多也是这套遇到问题搜得到答案。需要注意 JDK 版本与 Spring Boot 的匹配。Spring Boot 2.7 最高支持到 Java 17但你本机若是 Java 8 也完全能跑只是要保证 pom.xml 里的 java.version 和 IDE 的项目结构设置一致不然后面会踩「源发行版 17 需要目标发行版 17」这种编译警告。2.2 前后端分离还是模板渲染家政平台课设的真实取舍家政服务平台在真实产品里会有用户端小程序、服务人员 App、后台管理端三个界面。在课程设计时间窗口内做三套前端不现实常见做法是收敛成两套一套面向用户和服务人员的 H5 页面一套后台管理页面。在这之前你得先回答一个核心问题要不要前后端分离。我一般会跟人这样分析如果你的答辩环境网络不稳定或者你前端 JavaScript 基础一般用 Thymeleaf 做服务端模板渲染是风险最低的。后端把 ModelAndView 返回页面里写 th:each 遍历订单列表不需要解决跨域也不需要额外起 npm 服务。但如果你想把“提供 REST API 供外部调用”这条写进简历那就用 Vue 或微信小程序做消费者Spring Boot 只返回 JSON前后端通过跨域配置联调。无论选哪种后端接口都建议全部按 Controller - Service - Mapper 三层写Controller 只做参数接收和结果包装Service 里放业务规则Mapper 只碰 SQL。家政平台的「时间冲突检查」「订单状态流转」「评价后才能结算」这些规则是给面试官看的重点放在 Service 层才能被问到时讲清楚。2.3 搭建项目骨架pom.xml 与 application.yml 的最小配置用 IDEA 新建 Spring Initializr 项目坐标包名建议用 com.example.homeservice这会让后面所有包结构都跟着清晰起来。关键依赖只需要五个web、mysql、mybatis、lombok、validation。pom.xml 里最重要的部分是 Maven 编译器版本很多平台环境装的是 Java 8你代码里却用了 Java 17 的语法就会出现编译错误。properties java.version1.8/java.version maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.2/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency /dependencies这段配置里 java.version 决定编译字节码的版本maven.compiler.source 和 target 控制编译器读源码和生成 class 的版本。三者必须指向同一个版本否则 IDE 会按自己的默认 JDK 编译出现 Java 17 警告甚至 NoClassDefFoundError。Maven 仓库里有老版本依赖时应优先用 Spring Boot 父 POM 管理的版本号不要自己硬写 MyBatis 版本。application.yml 是另一个决定成败的文件。数据库地址、用户名密码、字符集、事务日志全部都在这里。我见过太多人把 MySQL 账号密码和 URL 写错启动时疯狂报 Access denied 或 Communications link failure这和环境变量配置乱套是同一类问题。最小可用配置如下server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/homeservice?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver application: name: home-service mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这里 URL 里的 useUnicodetrue 和 characterEncodingutf-8 是中文乱码的关键serverTimezone 不配会直接导致 java.sql.SQLException 说时区无法识别。map-underscore-to-camel-case 打开后数据库字段 service_name 能自动映射到 Java 的 serviceName省去给每一个字段写别名。log-impl 设为 StdOutImpl 是为了让 MyBatis 打印 SQL方便你后面排查查询条件和参数问题。启动成功后先在浏览器访问 localhost:8080能出现错误页也说明 Spring Boot 装配没问题接下来可以写数据表了。3. 家政平台数据库设计用户、服务人员与订单表怎么拆才不返工3.1 六张核心表从用户到评价的数据边界确定家政服务平台的业务边界比电商系统小核心只有六张表用户表、服务人员表、服务类别表、地址表、订单表、评价表。有人还会加一张用户收藏表或优惠券表但课程设计阶段加表不是越多越好每多一张表就多一组关联和测试数据。把订单表设计好整个项目的骨架就稳了。用户表存的就是手机号、昵称、头像、密码哈希服务人员表需要额外存服务类别、接单数、评分、状态地址表挂在用户下面用于下单时选择上门地址。订单表是唯一的事实表它要把用户、服务人员、时间、价格、地址、状态全部串起来。评价表独立因为订单完成后不一定马上评价而且可能支持追评。有一点容易被忽略服务人员表和订单表之间存在典型的一对多关系但订单表里建议冗余 service_name 和 user_name。这不是不做规范化的借口而是因为订单列表页要在同一个页面上展示服务人员、服务类别和用户每次都 join 三张表查询在演示时没问题在课程设计答辩讲解时也会显得你考虑了查询性能。真正的边界是不要冗余金额计算类字段比如订单价格只由 service_price 和时长决定不要额外把优惠后的价格也存进去。3.2 订单状态字段用状态机而不是一坨 if else订单表最不能省的是 state 字段。常见错误是把订单状态设计成普通字符串比如“待付款”“待安排”“服务中”然后在 Java 里用字符串比较。这在代码里会引发连锁问题只要一处把「待安排」写成「待派单」列表过滤和状态流转就全部错乱。正确做法是设计为 int 类型并且为每个取值定义一个常量类state状态名可流转到0待支付1, 61已支付待安排2, 62已接单3, 63服务中4, 64待评价5, 65已完成无6已取消无设计时把状态流转闭环定下来后面写 service 层每个方法时先检查当前状态是否允许目标状态而不是直接 setState。家政平台最容易出的业务错误是用户取消订单后保洁员还能接单服务完成后用户还能取消。这些都可以被状态机挡住。实际 Java 代码里OrderStatus 常量类可以配合 Map 做前置校验但课程设计不需要引入状态机框架一个小方法 checkStatus 就够了。3.3 建表 SQL 与初始化数据直接能跑的最小数据集订单表的 SQL 是整个数据库设计的核心其他表的 SQL 都可以照这个风格写。注意几个细节id 用 bigint 自增防止主键溢出金额字段用 decimal(10,2) 而不是 float所有表带 created_at 和 updated_at这在演示“申诉”“问题订单”时能派上用场关键外键要建索引但没有必要在每列上都建外键约束可以保留但要注意 MySQL 的锁范围。CREATE TABLE orders ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单号生成规则业务前缀时间戳随机数, user_id bigint(20) NOT NULL, worker_id bigint(20) NOT NULL COMMENT 服务人员ID下单时预占, category_id bigint(20) NOT NULL, service_name varchar(50) DEFAULT NULL COMMENT 冗余的服务名称, address varchar(255) NOT NULL, service_time datetime NOT NULL COMMENT 预约开始时间, service_duration int(11) NOT NULL DEFAULT 2 COMMENT 服务时长单位小时, total_price decimal(10,2) NOT NULL, state int(11) NOT NULL DEFAULT 0, remark varchar(255) DEFAULT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_state (user_id, state), KEY idx_worker_time (worker_id, service_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT家政订单表;这段 SQL 里 uk_order_no 是唯一索引保证订单号不会重复这是事务层之外的第二道防线。idx_worker_time 是核心当用户查询某个保洁员某个时间段是否空闲时通过索引快速扫描 orders 表里同一 worker_id 下重叠时间的记录避免全表扫描。service_duration 和 service_time 分开存是为了后面判断时间冲突时能计算区间[service_time, service_time duration hour)。如果没有这个索引页面上一点“立即预约”系统就会因为检索慢而卡住。初始化数据也不可少至少要有三类数据两条服务类别日常保洁、深度保洁三个服务人员一个测试用户。服务人员表要有 work_status 字段0 表示正常接单1 表示休息不然用户搜出来一个“休息中”的人还能下单就闹笑话了。4. 核心模块实现登录、预约下单与接单的 Java 代码怎么落地4.1 用 JWT 做双角色登录用户和服务人员共用一套鉴权家政平台的鉴权比后端管理员系统要复杂一档因为用户能下单服务人员能接单但两者又都不该访问管理后台。课程设计常见做法是用一个 role 字段区分在 JWT 的 payload 里带上 role拦截器里校验接口权限。这里用现成的 jjwt 库几行代码就能完成 token 生成。Component public class JwtUtil { Value(${jwt.secret}) private String secret; Value(${jwt.expire}) private long expire; // 小时 public String createToken(Integer userId, String role) { return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() expire * 3600 * 1000)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } public Claims parseToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } }JwtUtil 被 Spring 管理后每次调用 createToken 都会生成一个新的 token。secret 不能是空字符串否则启动时会直接抛 WeakKeyExceptionexpire 我一般设 24家政平台的用户不需要像支付系统那样频繁重新登录。parseToken 方法在拦截器里调用拿到 userId 和 role 后塞进 ThreadLocal这样 Controller 里就能直接通过 UserContext.currentUserId() 获取当前登录人不用每个接口都去解析一遍 token。接一个重要问题用户和服务人员登录表不是同一张但 JWT 生成逻辑是同一套。登录时先查用户表查不到再查服务人员表返回 token 的同时返回 role前端把它存在 localStorage 里。后续所有请求头部带上 Authorization: Bearer token拦截器只校验 token 有效性不查数据库。这有一个好处性能好不用每次请求都访问数据库但也要注意缺点服务人员被禁用后 token 不会立即失效课程设计演示时大家不太会追究这个。4.2 预约下单事务、时间冲突与防重复提交预约下单是家政平台最核心的 Service 方法也是面试官最可能追问的地方。它的业务规则是用户选择一个服务时间系统找到空闲的服务人员创建订单并返回。这里翻车最多的就是“时间冲突检查”。如果代码写成先 select 一下有冲突的订单再 insert 新订单两个用户同时在同一个时间段下单时两个请求都会读到同样的空闲状态然后双双插入成功同一个保洁员被安排到两个客户家里。正确解法是在事务里加锁。不要锁整张表那会拖慢所有查询而是对 worker_id 对应的一条服务人员记录执行 SELECT ... FOR UPDATE让同一个服务人员的预约请求串行化。这样做代价最小不同保洁员的预约互不阻塞同一保洁员的预约只能排队。Transactional(rollbackFor Exception.class) public Long createOrder(OrderCreateRequest request) { // 1. 锁住服务人员记录防止并发预约同一人 Worker worker workerMapper.selectByIdForUpdate(request.getWorkerId()); if (worker null || worker.getWorkStatus() ! 0) { throw new BusinessException(服务人员不可预约); } // 2. 在锁内检查时间冲突 LocalDateTime start request.getServiceTime(); LocalDateTime end start.plusHours(request.getServiceDuration()); int count orderMapper.countConflictOrder( request.getWorkerId(), start, end); if (count 0) { throw new BusinessException(该时间段已被预约); } // 3. 生成订单号并插入 String orderNo HO System.currentTimeMillis() RandomStringUtils.randomNumeric(4); Order order new Order(); order.setOrderNo(orderNo); order.setUserId(UserContext.currentUserId()); order.setWorkerId(request.getWorkerId()); order.setServiceTime(start); order.setServiceDuration(request.getServiceDuration()); // 后续价格计算省略 order.setState(0); orderMapper.insert(order); return order.getId(); }这个方法用 Transactional 确保锁、检查、插入在同一个数据库事务里。rollbackFor Exception.class 很重要因为 Spring 默认只在抛出 RuntimeException 时才回滚如果你自定义一个校验异常继承自 Exception又没有指定 rollbackFor就会遇到“订单插入失败但数据写进去了”的诡异问题必须显示声明。selectByIdForUpdate 对应的 Mapper 方法 SQL 是select * from worker where id #{id} for update。注意 for update 必须落在事务中而且务必要走主键索引否则行锁升级为表锁。步骤 2 的 countConflictOrder 查询条件写法如下这也是整个下单逻辑的另一个关键点select count(1) from orders where worker_id #{workerId} and state not in (5, 6) and service_time #{end} and date_add(service_time, interval service_duration hour) #{start}这个条件判断的是“已有订单的服务时间区间”与“新请求区间”是否重叠如果已有订单的开始时间早于新结束时间并且已有订单的结束时间晚于新开始时间那必然重叠。state not in (5,6) 过滤掉已完成和已取消的过期订单否则历史订单永远挡着未来预约。SQL 里不能用 service_time interval service_duration hour 这种方式比较因为 MySQL 的频率不一致会报错写成 date_add 函数最稳妥。4.3 接单和状态推进用条件更新完成乐观锁服务人员查看待接单列表然后点击接单这个动作听起来只是设置 state但如果用 update ... set state 2 where id ? 这种赤裸裸的更新用户可能刚取消订单保洁员后脚就接单成功流程被破坏。更可靠的是“条件更新 影响行数判断”。这是比 for update 更轻量的乐观锁。public void acceptOrder(Long orderId) { Integer workerId UserContext.currentWorkerId(); int rows orderMapper.updateStateByCondition( orderId, 1, 2, workerId); if (rows 0) { throw new BusinessException(订单状态已变化无法接单); } }对应 SQL 为update orders set state 2, updated_at now() where id #{orderId} and state #{expectedState} and worker_id #{workerId}这里 where 条件里的 expectedState 是 1含义是只有“已支付待安排”的订单才会被更新成“已接单”。如果另一个请求抢先修改了 state那么本次 update 的影响行数是 0业务层就能立刻感知并抛出提示。worker_id #{workerId} 是一个额外的防线防止保洁 A 修改保洁 B 的订单。这个模式可以复用到订单的每个流转点比如取消订单时 where state in (0,1,2)服务开始时 where state 2。条件更新在单表操作时比 for update 简单但要注意它不适合“先查询再更新”的复杂校验。家政平台里接单动作足够简单所以用乐观锁是恰当选择也方便你在答辩时说清楚高并发下乐观锁和悲观锁的取舍。4.4 列表查询与 JSON 映射两处绕不开的序列化坑用户下单后要看订单列表服务人员要看待接单列表列表查询如果返回的是嵌套了 Worker 对象的 Order 实体Jackson 序列化时会遇到 Java 对象深度拷贝和循环引用问题。常见现象是Order 里包含 WorkerWorker 里又包含 List 两边一互相引用接口直接返回 500 或无限递归。最简单的解法是列表数据不要直接序列化实体对象而是用 VO 对象承接。VO 只保留订单号、时间、服务人员姓名、状态文本这些展示字段数据库里的“详情”查询才用实体。写一个 toView() 方法手动值拷贝代码不漂亮但能保证不发疯。如果你不想为每个实体建 VO可以用 Jackson 的 JsonIgnoreProperties 注解打断循环但要记住这只影响序列化不影响业务。MyBatis XML 里联表查询也要注意别名。如果开启了下划线与驼峰映射表名和字段名列别名时不要写成select o.*, w.name from因为w.name在被映射到 Order 对象时找不到对应属性返回 null。正确写法是给查询结果定一个 ResultMap或者干脆查询后就使用订单表冗余的 service_name不要再 join worker 表。5. 家政平台实现中的常见坑与排查环境配置、乱码和并发冲突5.1 Java 环境变量配置和 Maven 编译版本不匹配现象在 IDEA 里能正常运行但命令行执行mvn clean package报错 “警告: 源发行版 17 需要目标发行版 17”或者直接 BUILD FAILURE。更隐蔽的是代码在本地跑得欢交到老师电脑上却无法启动报 java.lang.UnsupportedClassVersionError。原因命令行 Maven 使用的是 JAVA_HOME 指向的 JDK。你的 pom.xml 设 java.version1.8但 JAVA_HOME 环境变量配置指向 JDK 17于是编译器读到的 source/target 还是 IDE 里的旧缓存。这类项目往往在不同机器上复制来复制去Maven 仓库和 .idea 文件残留导致版本错乱。解决第一步命令行确认版本一致java -version和mvn -version两者显示的 Java 版本必须一样。不一样就重配环境变量Windows 下把 JAVA_HOME 指到 JDK 8 的安装根目录并在 Path 里移除多余项。这是纯环境问题改成整门程序设计课的 java 环境变量配置都是同一个套路。第二步回到 IDEA 的 Project Structure - Modules把语言级别改成 8再执行 Maven Reload。不要把希望寄托在 Maven 自动识别上它经常读的是本地仓库的缓存。5.2 中文乱码从数据库到 HTTP 至少有三个入口要设现象数据库里中文都变成了问号Postman 请求中文参数到后端变成乱码返回给浏览器的 JSON 中文显示成 \uXXXX 不常见但偶尔发生。三个乱码往往同时存在。原因数据库连接串没有设置字符集MySQL 建库时默认是 latin1Tomcat 读取表单参数时用 ISO-8859-1Spring Boot 处理 POST 请求体的编码不取决于 HTTP 头。只要一个环节不正确数据就再也救不回来。解决建库时显式指定 UTF-8CREATE DATABASE homeservice DEFAULT CHARACTER SET utf8mb4;。这是第一道闸门表里的中文乱掉以后只能 re-insert没有后悔药。连接串最前面必须有useUnicodetruecharacterEncodingutf-8像第 2 章配置里那样。最后在 Spring Boot 里加一个字符过滤器的 Bean强制 UTF-8 编码这一步能兼容多数 Tomcat 版本。如果还是乱码检查 MySQL 的全局变量show variables like character_set%确保 database 和 client 不是 latin1。5.3 服务人员同一时间段被重复预约先查再插必翻车现象用 Jmeter 或者两个浏览器窗口同时点击预约同一个保洁员同一个时间段的订单出现在两条记录里界面还提示都成功了。原因这正是第 4 章讲的并发冲突。两个请求都执行了 select count(1) 检查走的是普通快照读事务级别是默认的 REPEATABLE READ两个事务都看不到对方未提交的插入于是冲突检查双双通过。解决最低成本的方案是在下单前锁定服务人员行也就是 select ... for update。把冲突检查从普通读改成当前读事务 A 提交前事务 B 的 for update 会阻塞。如果因为某些原因不能用行锁也可以换一种思路先插入一条状态为“锁定中”的 order再通过唯一索引保证同一 worker 同一 service_time 不能重复插入但这样要额外处理“锁定过期”复杂度更高。课程设计阶段用 for update 是足够且能讲清楚的方案。5.4 订单列表 JSON 序列化循环引用现象访问订单列表接口页面全部 500控制台堆栈显示 StackOverflowError 或 Infinite recursion。你明明在其他接口里这样做过没问题。原因Hibernate 场景下是一对多集合懒加载导致才使用 session 就报了 LazyInitializationExceptionMyBatis 场景下最常见的是 Order 里嵌套 WorkerWorker 里又有一个 List Jackson 序列化时顺着内容无限往下找栈就爆了。这种现象被我们叫“黑匣子”报错不会直接告诉你在哪个字段你只能靠排查。解决最快修复是 Order 实体的 List 字段上加 JsonIgnore让它单边输出。更好的是构造一个订单详情 VO手动填充需要的字段彻底断开嵌套。还要检查 Entity 之间互相持有对方但不标记 Jackson 关系的代码把它当成一个纪律问题列表接口永远不直接返回关联集合。5.5 跨域配置写不对导致前端请求全部失败现象前端 Vue 项目用 axios 请求后端接口浏览器控制台报 “CORS policy”后端接口在 Postman 里完全正常。这又是一个跨域环境特有的坑而且只看后端项目日志很难发现因为请求根本没有进入你写的 Controller。原因浏览器同源策略拦截了响应。前后端分离模式下Vue 运行在 localhost:5173后端运行在 localhost:8080端口不同也算跨域。后端如果没有在响应头里加 Access-Control-Allow-Origin前端就收不到数据。解决Spring Boot 里写一个 WebMvcConfigurer 的配置类把允许来源设为你的前端地址不要图省事设为 * 加 allowCredentials(true)那种组合后端响应时不会有 cookie。课程设计时可以做成环境变量配置允许 localhost 和 127.0.0.1 同时访问。跨域配置不复杂但它经常是最后一个接上前后端的坎如果接口全部 500先看浏览器 Network 面板里有没有 CORS error避免在业务代码里找到天黑。6. 从演示原型到可部署系统验证清单与两个加分改进家政平台跑到这一步功能基本齐全但还要像交付一个项目那样验证而不是点几个按钮就结束。我的建议是先用 Postman 建立一条完整的订单链路请求集注册用户、登录获取 token、查看服务人员列表、创建订单、模拟支付、服务人员接单、开始服务、完成服务、用户评价、查看评价。每一步都检查响应里的 state 值是否符合预期。再额外写一个并发脚本对同一个 worker_id 的预约接口发起最多 20 个并发请求看有多少单被拒绝这是证明你状态机设计和 for update 有效的关键演示材料。两个加分改进可以按顺序做第一个是 Redis 缓存热数据把服务人员列表和分类列表缓存到内存请求不再每次都打 MySQL这个点能看出你对高性能的敏感度第二个是在支付成功后发一条异步通知用 Spring 自带的 Async 就能模拟不需要引入消息队列只要你讲得清楚异步和同步的区别效果和引入 RabbitMQ 一样。如果还想再加一个就做服务人员手写排期表比手工查订单表更贴近真实业务。我在做类似家政平台时最深的教训是没有先把订单状态图画清楚就写 Mapper结果后面每加一个“取消”或“售后”都要回头改动判断条件最后把状态流转收敛到一张表才稳住。这也是我愿意推荐你先花两小时把状态表定下来的原因。希望帮到你。本文还有配套的精品资源点击获取