Spring Boot家政服务管理平台:订单状态机与权限控制实战 又到选题季“基于Spring Boot的家政服务管理平台的设计与实现”这类题目几乎每年都能在课题清单里看到。这个题目看着不复杂无非是用户下单、管理员派单、家政人员接单再加点订单管理和评价管理。但真正动手做过的人都知道从需求拆分、数据库设计到接口落地每一步都有不少隐藏的门道尤其是订单状态流转和权限控制处理不好整个项目就会显得特别“学生气”。这篇文章把我自己做这类管理平台时的完整思路串起来讲一遍包括怎么拆需求、怎么选技术栈、订单状态机怎么建模、核心功能怎么落地以及在Spring Boot实战里那些高频报错的排查过程。无论是正在做课程设计或毕业设计的同学还是想快速积累一个完整后端项目的初学者都能照着这个思路搭出自己的版本。1. 家政平台的需求远比“用户下单”复杂先把角色和状态理清楚1.1 三方角色与权限边界很多人一拿到这个题目第一反应就是建几张表用户表、订单表、服务表然后开始堆CRUD。这样做出来的东西往往有一个通病所有接口对所有角色开放权限全靠前端隐藏按钮后端的Service层里谁都能改任何人的订单。这在演示的时候勉强能跑但一旦要谈设计思路几句话就被问住了。家政管理平台至少有三种截然不同的角色客户浏览服务项目、下单、支付、查看订单进度、对完工订单评价。家政人员查看被分配的订单、更新服务状态、维护自己的服务日历。管理员审核家政人员入驻、服务项目上下架、派单、处理纠纷、查看经营数据。这三类角色的关注点差异很大所以在设计接口时必须显式区分。我建议引入RBAC模型不一定要完整实现权限框架但至少要在用户表中加一个role字段并在Spring Boot里做一个自定义拦截器或注解在进入Controller之前校验当前用户是否有操作权限。比如“派单”这个动作只能是管理员操作“确认完工”只能是家政人员操作“提交评价”只能是下订单的客户操作。这些规则如果只写在Controller的业务判断里会非常散后面根本维护不住。我比较推荐的做法是定义一个RequireRole注解配合拦截器统一处理Controller里只需要在方法上标注允许哪些角色访问业务代码会干净很多。1.2 从下单到评价的主业务流程很多新手会把订单设计成简单的“未支付/已支付/已完成”三段式状态这个对这个项目来说是远远不够的。家政服务的核心在线下履约状态必须能反映履约全过程。我整理的主流程是这样的客户浏览服务项目选择服务时间、地点提交订单。订单生成后进入待派单状态管理员在后台看到新订单。管理员根据服务区域和家政人员排班进行派单。家政人员确认接单订单进入待服务状态。服务当天家政人员上门点击开始服务订单进入服务中状态。服务完成家政人员确认完工。客户确认服务结果可以发起售后或直接评价。评价完成后订单流转到已完成状态整个业务闭环结束。这个过程中还穿插着取消、超时、拒绝接单等分支。比如客户在待派单状态下可以取消订单管理员已派单但家政人员超过一定时间未接单系统要能自动重新分配或通知管理员。这些分支逻辑如果不在需求阶段梳理清楚写代码的时候就会这里补一个if那里补一个else非常痛苦。1.3 容易被忽略的非功能性需求评审老师或者面试官最喜欢问的一个问题是“你这个系统如果同时有100个人下单会怎么样”所以除了功能需求下面这些点也要提前想到订单号必须全局唯一且有规则比如日期随机数或者雪花ID不能直接用自增主键暴露业务量。金额计算必须用高精度类型Java里用BigDecimal数据库里用DECIMAL(10,2)不要用double或float。敏感操作要有日志谁在什么时间改了订单状态、改了服务价格都需要留痕。数据删除要用逻辑删除尤其是订单和用户数据物理删除会破坏业务统计和审计链路。并发控制同一个订单不能被两个管理员同时派单给不同的人这就要靠数据库乐观锁或状态机校验来解决。这些点乍一看不是“功能”但恰恰是让项目从“能跑”变成“有设计感”的关键。2. Spring Boot版本、持久层框架和鉴权方案技术选型不能拍脑袋2.1 Spring Boot版本选型3.x还是2.x这是被问得最多的问题之一。我的建议很直接2026年启动的新项目直接上Spring Boot 3.x配合JDK 17或21。原因有三个Spring Boot 2.x已经进入老版本维护周期新特性不再更新很多依赖也陆续停止兼容。Spring Boot 3.x基于Jakarta EE性能和安全基线更高内置的Spring 6也改进了一大批底层机制。现在大部分网上的坑和解决方案都已经沉淀到3.x反而不是问题。但是要注意Spring Boot 3.x有一个历史性变化javax.*包全部变成了jakarta.*。这意味着很多老教程里的代码拿到3.x下面直接编译报错典型的就是import javax.servlet.*要改成import jakarta.servlet.*。如果你的参考资料大多是2023年之前的文章选3.x前要有心理准备。如果学校教程还在教Spring Boot 2.x且你希望尽量少折腾那就选2.7.x它依然能用只是要有后续迁移的意识。另外要特别提醒Spring Boot版本不要盲目追新。比如刚发布的大版本第三方starter可能还没完全跟上选一个已经发布半年的稳定小版本更靠谱。比如Spring Boot 3.2.x或3.3.x在JDK17下非常稳相关整合资料也全。2.2 MyBatis-Plus还是Spring Data JPA持久层框架是这类项目绕不开的选择题。我用两种框架都做过管理系统下面是我的真实体感维度Spring Data JPAMyBatis-Plus单表CRUD效率高继承接口直接有现成方法高BaseMapper也直接有现成方法多表关联查询需要写JPQL或Specification学习成本高XML或注解SQL直观好控复杂动态SQL一般拼接条件比较别扭强条件构造器非常灵活数据库字段和实体映射命名策略自动映射但遇到复杂列名要花时间注解显式指定清晰明确团队上手成本需要理解持久化上下文、级联等概念会SQL基本就能写对这个项目来说我的推荐是MyBatis-Plus。理由是家政平台这类业务会有大量统计查询比如按时间段统计订单数、按服务分类统计销售额、按家政人员统计接单量这些用MyBatis-Plus的Wrapper条件构造器或者XML SQL写起来都很快。JPA写动态条件也不是不行但Specification那一套对新生来说确实有点劝退。如果你用MyBatis-Plus还有一个经验之谈条件构造器的lambdaQuery()可以避免硬编码字段名比如lambdaQuery().eq(Order::getStatus, status)这样字段改名的时候编译器能报错帮你兜底比写字符串安全得多。2.3 登录方案JWT还是Session管理平台登录鉴权选JWT还是Session要看项目的使用场景。纯后端API前端分离的项目我强烈建议JWT。JWT的核心优势是无状态后端不需要保存会话信息用户登录后拿到一个Token后续每次请求带在请求头里后端验签通过就放行。这对部署、扩展、前后端分离都友好。缺点是Token一旦签发在有效期内无法主动作废所以要在设计上处理好过期时间。具体方案上我不建议做毕业设计的人直接引入Spring Security它的配置复杂度对这个项目来说太高了。我自己常用的做法是登录成功后用io.jsonwebtokenJJWT生成Token。写一个JwtAuthenticationFilter继承OncePerRequestFilter在过滤器里解析Token并放入当前用户上下文。配合一个HandlerInterceptor做角色权限校验。自定义UserContext类用ThreadLocal保存当前登录用户信息。这套方案代码量不大逻辑清晰面试时也能把每个环节讲明白。后面我会把关键代码贴出来。2.4 接口风格RESTful其实够用但别过度设计很多教程会强调RESTful风格于是同学们就把所有接口都写成PUT /api/orders/{id}这种。RESTful本身没错但过度设计会给自己带来很大麻烦。比如订单取消、派单、确认完工这些动作本质上都是状态变更你非要通过PUT /api/orders/{id}传个status进去那状态机校验就得写在很深的地方非常绕。我建议的做法是资源的基础CRUD用RESTful涉及业务动作的接口直接用动词路径。比如POST /api/orders客户下单POST /api/orders/{id}/cancel取消订单POST /api/orders/{id}/assign管理员派单POST /api/orders/{id}/start家政开始服务POST /api/orders/{id}/complete确认完工POST /api/orders/{id}/review客户评价这样每个端点的语义非常清楚对应的状态流转也容易封装。别被所谓标准困住业务可读性优先。3. 数据库建模订单状态机画对了后端就成功了一半3.1 核心表结构六张基础表起步家政平台的表会比想象中多一点但也不是无限制铺开。我的经验是先建六张核心表后续按需扩展表名用途关键字段user用户表包含客户、家政人员、管理员id, username, password, role, phone, statusservice_category服务分类比如保洁、维修、搬家id, name, sort_orderservice_item具体服务项目属于某个分类id, category_id, name, price, unit, descriptionservice_order订单主表整个系统的核心id, order_no, customer_id, worker_id, item_id, status, amount, address, service_timereview评价表客户对已完成订单评价id, order_id, customer_id, rating, content, create_timeoperation_log操作日志记录敏感操作id, user_id, action, detail, create_time以订单表为例建表语句大致长这样CREATE TABLE service_order ( id BIGINT NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL 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 COMMENT 订单状态1待派单 2已派单 3服务中 4待评价 5已完成 6已取消, amount DECIMAL(10,2) NOT NULL COMMENT 订单金额, pay_status TINYINT NOT NULL DEFAULT 0 COMMENT 支付状态0未支付 1已支付, address VARCHAR(255) NOT NULL COMMENT 服务地址, service_time DATETIME NOT NULL COMMENT 预约服务时间, customer_note VARCHAR(500) DEFAULT NULL COMMENT 客户备注, cancel_reason VARCHAR(255) DEFAULT NULL COMMENT 取消原因, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted TINYINT NOT NULL DEFAULT 0 COMMENT 逻辑删除标记, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_customer_id (customer_id), KEY idx_worker_id (worker_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT家政服务订单表;这里有几个细节值得说明order_no必须加唯一索引这是防重复下单的第一道防线。status字段用TINYINT存数字状态比直接存字符串更节省空间但代码里一定要定义常量类不能到处写魔法数字。所有表都要有create_time和update_timeMySQL的ON UPDATE CURRENT_TIMESTAMP可以自动维护更新时间。deleted逻辑删除字段几乎必加后面接MyBatis-Plus的TableLogic非常方便。3.2 状态机如何落到代码里订单状态的难点不在建表而在控制状态流转的合法性。比如“待派单”可以直接“取消”但“服务中”不能直接“取消”“已完成”的订单不能再被派单。如果这些规则散落在各个Service方法里时间一长肯定会有遗漏甚至出现数据错乱。我比较推荐在服务层做一个统一的状态机校验类用静态Map维护合法流转路径public class OrderStatusMachine { public static final int PENDING 1; // 待派单 public static final int ASSIGNED 2; // 已派单 / 待服务 public static final int IN_PROGRESS 3; // 服务中 public static final int PENDING_REVIEW 4; // 待评价 public static final int COMPLETED 5; // 已完成 public static final int CANCELLED 6; // 已取消 private static final MapInteger, SetInteger TRANSITIONS new HashMap(); static { TRANSITIONS.put(PENDING, new HashSet(Arrays.asList(ASSIGNED, CANCELLED))); TRANSITIONS.put(ASSIGNED, new HashSet(Arrays.asList(IN_PROGRESS, CANCELLED))); TRANSITIONS.put(IN_PROGRESS, new HashSet(Arrays.asList(PENDING_REVIEW, CANCELLED))); TRANSITIONS.put(PENDING_REVIEW, new HashSet(Arrays.asList(COMPLETED))); // 已取消、已完成是终态不允许再迁移 } public static void validate(int currentStatus, int targetStatus) { SetInteger allowed TRANSITIONS.get(currentStatus); if (allowed null || !allowed.contains(targetStatus)) { throw new BusinessException(非法状态流转 currentStatus - targetStatus); } } }然后在订单Service里任何状态变更方法第一步都是调用这个校验public void assignOrder(Long orderId, Long workerId) { Order order orderMapper.selectById(orderId); if (order null) { throw new BusinessException(订单不存在); } OrderStatusMachine.validate(order.getStatus(), OrderStatusMachine.ASSIGNED); order.setStatus(OrderStatusMachine.ASSIGNED); order.setWorkerId(workerId); orderMapper.updateById(order); // 记录操作日志…… }这样状态迁移规则集中在一个类里逻辑一目了然后面扩展新状态也只需要改这一个地方。设计模式里的状态模式在这个场景下也能用但我觉得对毕业设计来说这个Map方案足够清晰而且更容易讲明白。3.3 数据库设计中的几个细节除了表结构和状态机还有几个细节会影响后面的开发效率金额字段一律用DECIMALMySQL中的浮点类型FLOAT/DOUBLE会有精度问题尤其在做统计求和时容易出“灵异现象”。Java侧对应BigDecimalMyBatis-Plus的映射也很干净。状态字段用数字还是字符串数字存储效率高但可读性差字符串直观但容易拼错。我建议用数字加常量类辅以项目里写一个数据字典说明文档。面试时如果你能主动提到状态码与常量的映射规范会是个加分项。外键最好不用很多教学案例会建外键约束但在真实项目里外键会导致插入更新性能下降也不方便逻辑删除。我习惯只用逻辑关联也就是表里存对方的主键ID不建立物理外键约束由应用层保证一致性。面试被问到“为什么不用外键”时这是一个很成熟的回答点。索引不是越多越好像customer_id、worker_id、status这种高频查询字段加索引即可别每个字段都加否则写操作的性能会受影响。4. 核心代码落地的关键节点JWT登录、订单流转与幂等控制4.1 JWT登录与自动续签登录接口的逻辑不复杂查用户、比对密码、签发Token。但Token的有效期和续签方案是需要提前想清楚的。我常用的Token结构是把userId、role放到JWT的Claims里有效期设置为2小时。续签策略采用“滑动续期”在拦截器解析Token时如果发现剩余有效时间不足30分钟就重新签发一个有效期2小时的新Token放进响应头X-Token里前端发现这个头就更新本地存储的Token。关键代码大致如下public String generateToken(Long userId, String role) { Date now new Date(); Date expiry new Date(now.getTime() EXPIRE_MILLIS); return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(role, role) .setIssuedAt(now) .setExpiration(expiry) .signWith(secretKey, SignatureAlgorithm.HS256) .compact(); }续签放在过滤器里做Claims claims Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .getBody(); long remainMillis claims.getExpiration().getTime() - System.currentTimeMillis(); if (remainMillis REFRESH_THRESHOLD_MILLIS) { String newToken generateToken(Long.valueOf(claims.getSubject()), claims.get(role, String.class)); response.setHeader(X-Token, newToken); }这个方案的好处是用户无感续签不会用着用着突然被踢下线。对毕设演示来说这种体验非常加分因为不用频繁重新登录。密码存储也值得提一句不要明文存数据库至少用BCryptPasswordEncoder做哈希。Spring Security分离出来之后可以单独引入spring-security-crypto依赖只用来做密码加密不会带来全套Spring Security的复杂度。4.2 订单流转的Service层封装有了状态机校验之后订单Service的方法会非常清晰。我会把一组状态变更操作封装成类似下面这样的结构Service RequiredArgsConstructor public class OrderService { private final OrderMapper orderMapper; private final OperationLogMapper operationLogMapper; Transactional public void cancelOrder(Long orderId, Long operatorId, String reason) { Order order getOrderById(orderId); OrderStatusMachine.validate(order.getStatus(), OrderStatusMachine.CANCELLED); order.setStatus(OrderStatusMachine.CANCELLED); order.setCancelReason(reason); orderMapper.updateById(order); saveLog(operatorId, CANCEL_ORDER, 订单 order.getOrderNo() 取消); } }注意这些方法都要加Transactional因为状态变更往往伴随操作日志写入任何一个步骤失败都不能留下半截数据。如果订单状态变了但日志没写上后面排查问题会很痛苦。派单流程比取消稍微复杂一点因为要同时校验两件事该订单处于待派单状态且家政人员存在且可接单。数据库设计上可以考虑给service_order.worker_id加索引这样后续查询“某位家政人员负责的所有订单”会快很多。4.3 防重复下单与接口幂等家政平台最容易出现的重复操作就是客户疯狂点击“提交订单”按钮导致同一时间生成多条相同订单。这个问题的标准解法是接口幂等。我常用的方案有三种按成本从低到高排列数据库唯一索引订单号全局唯一重复插入直接报错这是最后一道防线。前端按钮防抖提交后立即禁用按钮简单有效但不能防接口重放。后端幂等键客户端在创建订单时生成一个requestId后端用Redis的SETNX命令判断这个请求是否已经处理过。如果是毕业设计我建议至少实现第一种加第三种组合。后端幂等键的代码和Redis缓存类似public Long createOrder(CreateOrderRequest request) { String idempotentKey order:create: request.getRequestId(); Boolean first redisTemplate.opsForValue() .setIfAbsent(idempotentKey, 1, Duration.ofMinutes(5)); if (!Boolean.TRUE.equals(first)) { throw new BusinessException(请勿重复提交); } // 继续创建订单…… }这样即使前端防抖失效后端也能挡住大部分重复请求。接口幂等性在面试里经常被问到能主动讲清楚这个设计项目深度一下就上来了。4.4 超时未支付/未接单的定时处理订单还会涉及“超时未处理”的场景比如客户下单后30分钟不支付系统应该自动取消管理员派单后家政人员2小时不接单系统应该通知管理员重新分配。这个功能不需要引入炫技的分布式任务调度Spring Boot自带的Scheduled就够用。我的做法是Service public class OrderTimeoutTask { Scheduled(cron 0 */5 * * * ?) public void cancelExpiredUnpaidOrders() { LocalDateTime deadline LocalDateTime.now().minusMinutes(30); ListOrder expiredOrders orderMapper.selectList( new LambdaQueryWrapperOrder() .eq(Order::getPayStatus, 0) .eq(Order::getStatus, OrderStatusMachine.PENDING) .lt(Order::getCreateTime, deadline)); for (Order order : expiredOrders) { // 批量更新为已取消并记录日志 } } }注意Scheduled默认是单线程执行的如果你的项目里同时有好几个定时任务配置一个线程池更稳妥Configuration public class SchedulingConfig implements SchedulingConfigurer { Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(Executors.newScheduledThreadPool(4)); } }定时任务在演示时不太好展示因为要等30分钟所以通常可以把超时时间配置到配置文件中演示时临时改成1分钟等过期后看数据变化。5. 从自动装配失效到跨域拦截Spring Boot实战高频坑复盘5.1 MyBatis-Plus与Spring Boot 3的版本坑Spring Boot 3.x升级之后MyBatis-Plus的依赖坐标发生了变化这是很多同学第一周就会踩的坑。如果你用的是Spring Boot 3.xmaven依赖要写成mybatis-plus-spring-boot3-starter而不是mybatis-plus-boot-starter。dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version3.5.7/version /dependency另外3.5.9版本之后MyBatis-Plus把分页插件等很多内置功能拆到了单独的mybatis-plus-jsqlparser依赖里。如果发现分页不生效多半是少了这个依赖。最简单的排查方式就是看启动时是否报ClassNotFoundException然后根据类名快速定位缺少的包。还有一个高频问题PaginationInnerInterceptor注册了但分页无效。如果你的分页插件定义在配置类里但MyBatis-Plus内置的Spring Boot自动配置没有生效大概率是扫描路径的问题。我建议在启动类上显式加MapperScan(com.xxx.mapper)比依赖自动配置更可控。5.2 自动装配失效的排查思路Spring Boot的自动装配很强但偶尔也会“失灵”。最常见的报错是Description: Field userMapper in com.xxx.service.UserService required a bean of type com.xxx.mapper.UserMapper that could not be found.这个错误的本质是Mapper接口没有被Spring容器扫描到。排查思路按下面三步走检查启动类上有没有MapperScan扫描路径是否正确包名是不是写错了。检查Mapper接口有没有加Mapper注解虽然MyBatis-Plus不强制要求但加上了更稳。检查启动类的位置SpringBootApplication所在的包必须是所有子包的父级否则Spring组件扫描扫不到子模块。另外还有一个容易被忽略的点同一个服务里如果同时使用了feign、mq等starter它们的自动装配有时候会互相影响。但这在纯Spring Boot单体项目里不常见遇到时优先怀疑包路径而不是去怀疑框架本身。5.3 跨域与时间序列化这类“小问题”前后端分离开发时跨域问题几乎必现。Spring Boot解决跨域最直接的方式是配置一个WebMvcConfigurerConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }但如果你同时使用了Spring Security或自定义拦截器要记住一个顺序问题预检请求OPTIONS不需要经过业务拦截器。所以拦截器里要放行OPTIONS方法否则前端会报跨域错误但后端日志里根本没有业务代码的记录排查半天才发现是拦截器把预检请求拦截了。时间序列化也是一个隐藏的小坑。MySQL存储的DATETIME映射到Java的LocalDateTime后如果直接作为JSON返回默认序列化格式会带T比如2026-05-01T10:30:00。前端解析起来很别扭。我习惯在application.yml里统一配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8如果项目里用了LocalDateTime还需要额外配置JavaTimeModule或者引入jackson-datatype-jsr310不过Spring Boot 2.x之后默认就带了这个模块通常只要配格式就行。时区这里特别容易忘不设置GMT8的话前端看到的时间可能比实际时间早8个小时尤其是在服务器部署到海外或者Docker容器里时特别明显。5.4 关于服务发现、读取文件与启动配置的补充这个小节本来没想写但确实有太多同学在项目已经能跑起来之后还要被一些小问题卡一晚上。端口被占用前后端分离项目如果后端起了8080端口前端Vue项目起在8081或5173两个端口要区分开。后端跑不起来时报Port already in use用netstat -ano | findstr 8080找到进程杀掉就行。静态资源配置如果你把上传的家政服务图片放在项目本地目录需要注意Spring Boot默认的静态资源路径并不包含你的自定义目录。要用spring.web.resources.static-locations配置或者用WebMvcConfigurer手动映射/upload/**目录。Banner网上那些生成文字Banner的工具确实好玩但建议放到个人练习项目里就好了课程设计和毕设就默认吧别把精力花在这上面。项目日志级别、开发环境和生产环境配置分离这些才是该花时间的点。6. 从能跑到能展示单测、演示数据与交付配置6.1 最值得写的几个单元测试很多人不写测试觉得麻烦。但答辩时如果被问到“你这个项目怎么保证正确性”你能说“我测过了”比什么都强。我这里推荐写几个最值得写的测试不用覆盖全量接口但把核心逻辑保护起来订单状态机测试验证合法流转成功、非法流转抛异常。登录接口测试正确密码返回Token错误密码返回401。下单接口幂等测试相同请求连续提交两次第一次成功第二次报“请勿重复提交”。定时任务测试造一条超时未支付订单手动触发任务方法断言订单状态被修改为已取消。如果用了Spring Boot的MockMvc写Controller测试非常方便。核心是给每个Service都加上Transactional使得测试完成后回滚避免污染本地数据。6.2 一套能直接演示的状态数据项目交付时最尴尬的事情是点开订单列表里面全是空数据或者只有一种“已完成”的订单演示效果大打折扣。我建议写一个DataInitializer在项目启动时自动写入演示数据。演示数据要覆盖每一个状态待派单、已派单、服务中、待评价、已完成、已取消各来两条家政人员服务日历里有几条可分配的空档管理员后台能看到今日本地订单量和营收统计有曲线可看。这样无论从哪个页面开始演示都能展示出完整业务链路。数据初始化类可以放在启动器里用ApplicationRunner执行只在特定profile下生效避免污染真实数据。启动时判断一下数据库里是否已有数据没有才插入有就直接跳过避免重复执行。6.3 打包部署与运行参数到了交付阶段跑一个mvn clean package -DskipTests把项目打成Jar包然后用java -jar xxx.jar启动。线上部署的时候有几个参数建议带上java -jar -Xms256m -Xmx512m -Dspring.profiles.activeprod housekeeping-platform.jar新建一个application-prod.yml把数据库密码、Redis地址等环境相关配置放进去日志输出到指定文件而不是控制台。这样无论是放在云服务器上给老师演示还是让别人体验项目都不会因为环境差异导致启动失败。如果还想进一步“有成品感”可以写一个简单的Dockerfile用openjdk:17-jdk-alpine作为基础镜像把Jar包打进去一条命令启动整个后端环境。但Docker的坑也不少Windows下路径挂载、网络模式、内存限制等都会影响体验我不建议把它作为必选项目有富余时间再研究。6.4 项目文档的几个关键内容最后补一点关于交付文档的体会。很多同学以为项目做完就结束了其实答辩和提交材料时项目说明文档占的权重不低。我建议至少把这几样东西准备好需求分析文档画清楚角色、用例和核心流程。数据库设计文档每张表的字段注释写清楚附上ER图。接口文档用Apifox或Postman导出一份接口文档标清楚请求参数和响应示例。部署文档从环境准备到启动命令一步步写清楚哪怕再过两个月你自己也能照着跑起来。这几份文档不用写得多华丽但一定要和代码实际情况一致。我最怕看到“文档里写的是V2.0代码还在V1.0”的情况查起来非常浪费时间。最后再分享一个小技巧我在交付类似项目时习惯给自己留一个“最终验证清单”用全新环境从头部署一遍从建数据库、改配置、启动项目到演示核心业务整个流程走一遍看有没有遗漏的步骤。这个环节看起来简单但每次都能发现几个问题比如某张表忘记初始化、某个配置写死在本机路径、某个接口在空数据时直接报500等。家政服务管理平台这种题目虽然看着普通但它完整覆盖了一个业务系统从需求到部署的全部环节认真做完之后你对Spring Boot的理解绝对会上一个台阶。如果你正在做这个项目我希望这篇文章能帮你少走一点弯路把时间花在真正有价值的设计和实现上。