资讯动态

基于SpringBoot的家政服务系统:订单状态机与数据库设计实战解析

发布时间:2026/9/23 20:10:38 来源:尧图企业网站定制
简介面向Java后端学习者与毕业设计人群这是一份基于SpringBoot的家政服务平台系统全套源码包整合前端Vue页面、后端Java逻辑、数据库脚本及论文文档可直接用于课程设计、大作业或工程实训也可作为二次开发的基础工程。压缩包内共949个文件以179个Java源码、164个JavaScript脚本、61个Vue组件、62个HTML页面为核心辅以162个SVG图标、75张JPG素材和53个CSS样式表同时包含1个SQL数据库文件以及Maven配置、项目说明等配套内容整体仅17.71MB轻量且结构完整。目前已有87人学习下载适合希望快速上手Spring Boot全栈项目或准备毕设答辩的读者。资源附带一键安装、运行、构建脚本和备份文件在主流IDE中导入即可启动登录、权限、业务模块等代码层次清晰便于参照扩展家政预约、服务管理等功能。1. 家政服务平台的SpringBoot单体架构为什么适合毕设与项目复现拿到“基于Java的家政服务平台系统”这套资源我第一件事不是看代码而是先确认它的技术栈。从目录结构与数据库脚本来看它走的是SpringBoot MyBatis MySQL的经典路线前端用模板页面渲染整个工程属于单体应用这恰恰是本科毕业设计和高分项目里最稳的组合既能体现业务建模能力又能把权限、状态机、报表查询几个考点全部覆盖。资源包里全套源码、数据库sql和论文是齐的导入数据库后把配置一改就能起服务。家政服务的业务链条不长但足够完整——用户下单、服务人员接单、管理员审核、订单状态流转每一步都能对应到数据库SQL和接口实现。如果你正需要一份能复现、能讲清来龙去脉的Java毕设源码下面按“表结构 → 核心流程 → SQL优化 → 部署演示”的顺序拆解所有命令都是我在本机验证过的常规做法。2. SpringBoot分层架构与数据库设计订单、服务人员、用户的表关系怎么落2.1 项目分层Controller / Service / DAO 的边界家政服务平台作为典型的业务系统代码结构如果按“controller → service → mapper”三层来组织答辩时最容易被认可。我在拆这套源码时发现它的包名以com.home.service开头controller里只放参数接收和结果封装service里写事务和业务规则mapper只做SQL映射。这样做的原因是家政订单涉及金额和服务时长事务必须放在service层否则多个表的数据很容易不一致。常见的一个误区是把查询逻辑直接写在controller里。我一般会坚持在service接口里定义方法然后实现类加Override和Transactional。比如创建订单时要同时插入订单主表和订单明细表还要更新服务人员的可接单数量这三个操作必须在一个事务里否则用户支付后服务人员却没被锁定。源码里这个边界很清楚抄作业的同学只需要注意别把事务注解加到controller方法上因为Spring的AOP代理默认只对service生效。下面是一个典型的service方法片段来自订单模块Service public class OrderServiceImpl implements OrderService { Autowired private OrderMapper orderMapper; Autowired private OrderDetailMapper orderDetailMapper; Autowired private WorkerMapper workerMapper; Override Transactional(rollbackFor Exception.class) public int createOrder(OrderVO vo) { // 1. 校验服务人员是否处于可接单状态 Worker worker workerMapper.selectById(vo.getWorkerId()); if (worker null || !1.equals(worker.getStatus())) { throw new ServiceException(该服务人员暂不可接单); } // 2. 插入订单主表返回自增主键 Order order new Order(); order.setUserId(vo.getUserId()); order.setWorkerId(vo.getWorkerId()); order.setStatus(0); // 0待派单 1服务中 2待付款 3已完成 4已取消 order.setTotalAmount(vo.getTotalAmount()); orderMapper.insert(order); // 3. 插入订单明细注意订单id要回填 for (OrderItem item : vo.getItems()) { item.setOrderId(order.getId()); orderDetailMapper.insert(item); } // 4. 订单创建后占用服务人员资源 workerMapper.updateStatus(vo.getWorkerId(), 0); return order.getId(); } }这里插入订单主表后MyBatis的useGeneratedKeystrue会把自增id回填到order.getId()这是常见的坑。如果你发现订单明细外键都是0通常就是没有配置useGeneratedKeys或者KeyProperty写错。rollbackFor Exception.class确保任何异常都回滚默认的Spring事务只回滚RuntimeException这一点在答辩时经常会有人问。2.2 数据库表设计与SQL脚本数据库脚本在资源包里是home_service.sql我用MySQL 8.0导入时没有报错字符集是utf8mb4。整个库一共6张基础表用户表、服务人员表、服务类别表、订单表、订单明细表、管理员表。没有做过分的冗余基本符合3NF也方便在论文里画E-R图。2.2.1 用户表与服务人员表用户表承接登录和下单服务人员表单独拆出来而不是挂在用户表上加角色字段目的就是让服务人员有独立的评分、接单次数、服务状态等字段。注意服务人员的status字段我用的是char(1)因为只有固定几个值不需要tinyint用字符串在写业务代码时可读性更好。下面是两张表的核心列表名t_user字段类型说明idint主键自增usernamevarchar(50)登录名唯一索引passwordvarchar(255)BCrypt加密后的密码phonevarchar(20)手机号接收通知statuschar(1)0正常 1冻结表名t_worker字段类型说明idint主键自增namevarchar(50)服务人员姓名service_typeint关联t_service_type.idscoredecimal(2,1)评分5.0为满分order_countint累计接单数statuschar(1)0可接单 1休息 2离职我之前见过一份毕设源码把服务人员信息全塞在用户表里结果申请一个“保洁”业务时service_type在用户表里语义不清晰。拆表的成本很低但能让后面写统计SQL时少绕很多弯。2.2.2 订单表与订单明细表订单主表记录一次服务的整体信息明细表记录同一个订单里包含了几项具体服务。比如用户预约一次“厨房深度保洁”可能包含“油烟机清洗”和“台面去污”两个服务项。这样的设计在论文里可以写“一对多关系的建模与实现”是加分点。CREATE TABLE t_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 业务单号, user_id INT NOT NULL COMMENT 下单用户, worker_id INT COMMENT 服务人员派单前为空, service_time DATETIME COMMENT 预约上门时间, address VARCHAR(200) COMMENT 服务地址, total_amount DECIMAL(10,2) COMMENT 订单金额, status CHAR(1) DEFAULT 0 COMMENT 0待派单 1服务中 2待付款 3已完成 4已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;order_no用业务单号而不是直接暴露自增id是为了在支付回调对账时避免遍历不确定的订单号。status用char类型并注释清楚每个值是这类系统的通行做法。我一般还会加上create_time的索引因为列表页和报表都要按时间过滤。然后是订单明细表CREATE TABLE t_order_detail ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL COMMENT 订单主表id, service_type_id INT NOT NULL COMMENT 服务类别id, service_name VARCHAR(50) COMMENT 冗余服务名称, quantity INT DEFAULT 1 COMMENT 数量, unit_price DECIMAL(10,2) COMMENT 单价, KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;service_name在这里做冗余存储是为了列表页不每次都join服务类别表。如果在答辩中问“为什么冗余”可以说查询频率远高于写入频率固定数据用空间换性能是合理的。再强调一下导入数据库sql时如果用Navicat直接运行脚本先检查第一行是否有CREATE DATABASE有的话在连接层执行即可没有的话需要手工创建数据库再选择source文件。资源包里的脚本我确认过包含建库语句但不同版本的MySQL对COMMENT语法的兼容性可能有差异遇到报错就把COMMENT部分去掉再导入。3. 家政服务核心流程实现从用户下单到服务人员接单的状态流转3.1 用户下单接口的请求与响应设计在前后端不分离的SpringBoot项目中用户下单通常以表单提交后端用统一响应对象包装结果。源码里定义了一个Result类code200表示成功code500表示业务异常。接口路径是/order/create参数包括用户id、服务人员id、预约时间、地址、服务项列表。注意这里不建议直接把实体类作为接收参数而是用OrderVO否则前端多传一个status字段就能伪造订单状态。PostMapping(/order/create) ResponseBody public Result createOrder(RequestBody OrderVO vo) { // 校验登录状态 User user (User) request.getSession().getAttribute(loginUser); if (user null) { return Result.error(请先登录); } vo.setUserId(user.getId()); int orderId orderService.createOrder(vo); return Result.success(orderId); }这里从session中拿用户是SpringBoot单体应用最常规的做法。如果你的项目用了JWT或者Redis做登录态接口本身并不需要改动只需要在拦截器里把解析好的用户id塞到ThreadLocal即可。参数用RequestBody接收JSON时前端要设置Content-Type为application/json否则表单格式会解析不到对象。这一点在联调时最容易出错现象是报“Required request body is missing”。3.2 服务人员接单与状态机家政平台最核心的部分是订单状态流转。我在源码里看到的状态定义是一个枚举类而不是散落的魔法数字这一点很值得在论文里展开。枚举的好处是switch的时候不许写错字符并且可以集中定义可流转的下一个状态。当前状态可流转状态操作人0待派单1服务中、4已取消服务人员/用户1服务中2待付款服务人员2待付款3已完成用户3已完成无-3.2.1 订单状态枚举与流转校验public enum OrderStatus { PENDING(0, 待派单), SERVING(1, 服务中), WAIT_PAY(2, 待付款), FINISHED(3, 已完成), CANCELLED(4, 已取消); private final String code; private final String desc; OrderStatus(String code, String desc) { this.code code; this.desc desc; } public boolean canChangeTo(String target) { switch (this) { case PENDING: return 1.equals(target) || 4.equals(target); case SERVING: return 2.equals(target); case WAIT_PAY: return 3.equals(target); default: return false; } } }canChangeTo方法限制了非法流转比如待派单只能到服务中或取消不能直接跳到已完成。实际业务里还要校验操作人的身份比如“服务中”只能由服务人员点击“待付款”只能由用户发起支付。把状态枚举单独抽出来写单元测试时可以构造所有状态迁移的组合这样答辩时能直接展示测试用例比空口讲逻辑更扎实。3.3 三种角色共用一套登录态与权限拦截器平台里有用户、服务人员、管理员三个角色。源码中并没有为每个角色写一个独立的登录接口而是用role_type字段区分登录成功后把角色类型写入session。然后通过 HandlerInterceptor 判断当前请求需要什么角色。三个角色请求路径的前缀不同/user/**、/worker/**、/admin/**用拦截器做路径匹配。public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser request.getSession().getAttribute(loginUser); String uri request.getRequestURI(); if (loginUser null) { response.sendRedirect(/login); return false; } User user (User) loginUser; if (uri.startsWith(/admin/) !3.equals(user.getRoleType())) { response.sendError(403); return false; } return true; } }这个拦截器很薄但面试官常问“为什么不直接用Spring Security”。常见做法是毕设项目为了控制复杂度用拦截器配合Session更直观如果你想体现自己的水平可以说明Spring Security的过滤链在这里需要配置多种用户类型配置成本比拦截器高对于单体模板项目并不划算。当然如果你的毕设要求是“基于Spring Security的权限控制”那就要换成 UserDetailsService 实现逻辑是相通的。注意拦截器需要注册Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new AuthInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /static/**); } }excludePathPatterns必须把静态资源排除否则拦截器会拦截css和js导致页面样式丢失。这是我见过最多的启动后白屏问题。4. 数据库SQL使用技巧与高仿真的演示数据构造4.1 订单统计报表的常用SQL写法家政服务平台的论文里通常要放几张统计截图比如“近7日订单量”“评分最高的服务人员Top10”。这些数据用单个SQL就能查出来不需要额外写Java代码。下面是最常用的一个按天统计订单数的查询SELECT DATE(create_time) AS day_count, COUNT(*) AS order_num FROM t_order WHERE create_time DATE_SUB(CURDATE(), INTERVAL 6 DAY) GROUP BY DATE(create_time) ORDER BY day_count;DATE_FORMAT可以换成DATE(create_time)返回的是日期更方便Java的Date类型接收。这里有个隐藏坑订单表数据量达到几十万时DATE函数会导致create_time上的索引失效因为对索引列做了函数运算。生产环境通常改成范围查询create_time 2025-01-01 00:00:00 AND create_time 2025-01-02 00:00:00。毕设数据量小怎么写都能跑但答辩时能说出来这一点会显得认真。服务人员排行报表需要关联订单表和服务人员表SELECT w.name, COUNT(o.id) AS order_count, ROUND(AVG(o.score), 2) AS avg_score FROM t_order o JOIN t_worker w ON o.worker_id w.id WHERE o.status 3 GROUP BY w.id, w.name ORDER BY order_count DESC LIMIT 10;注意这里订单表里需要保存评分字段或者另建评分表。AVG只统计已完成订单避免未完成数据影响平均值。4.2 索引使用与连表查询的取舍数据库sql脚本中t_order表的worker_id字段上一般会建普通索引因为后台列表页要按服务人员过滤。但只写SELECT * FROM t_order WHERE worker_id ?并不够还要考虑排序字段。我建议在订单列表页的SQL中让排序字段也走索引否则MySQL会额外做filesort。场景推荐索引说明用户查自己的订单idx_user_id(user_id)高频查询必须加管理员按时间查订单idx_create_time(create_time)支持范围统计按状态过滤status区分度低不建议单独建订单号查询uk_order_no(order_no)业务唯一约束status字段只有5个值区分度太低单独建索引经常不被MySQL优化器使用反而增加写入成本。我一般会拿status create_time做联合索引覆盖“查某状态下某时间范围”的场景。4.3 演示数据怎么填才像真实业务拿到源码后直接用里面的样本数据当然可以但答辩时评委翻数据库看到用户名叫“admin1、admin2”会扣印象分。我一般会清空业务表用存储过程批量生成演示数据。比如生成100个用户和500条订单DROP PROCEDURE IF EXISTS gen_demo_data; DELIMITER $$ CREATE PROCEDURE gen_demo_data() BEGIN DECLARE i INT DEFAULT 1; WHILE i 100 DO INSERT INTO t_user (username, password, phone, status) VALUES (CONCAT(user_, i), $2a$10$..., CONCAT(138, LPAD(i, 8, 0)), 0); SET i i 1; END WHILE; END$$ DELIMITER ; CALL gen_demo_data();密码字段需要是BCrypt加密后的值你可以在任意一个注册接口里调一次加密方法把输出的密文复制过来这样登录时校验才会通过。LPAD函数让手机号长度固定为11位避免展示时参差不齐。订单表可以用随机时间生成近90天的数据这样统计曲线图比较平滑。注意生成数据后要更新统计计数比如t_worker表的order_count要与实际订单数一致否则后台首页的“接单量”会显得很假。5. 打包部署与答辩演示几个让系统在现场跑稳的细节5.1 Maven打包与外部配置多数人拿到这套源码后第一步是直接改数据库密码然后启动。实际上更稳的方式是把配置从代码里拆出来。SpringBoot的application.yml默认在resources目录下但打成jar后改配置必须重新打包现场演示时很不方便。我一般会在jar同目录建config/application.yml把数据源、日志级别全部放进去。启动命令变成mvn clean package -DskipTests java -jar home-service.jar --spring.config.location./config/application.yml--spring.config.location指定外部配置文件后内部resources里的配置会被忽略。如果你的源码里没有这个外置习惯至少要在答辩前确认MySQL连接串中的url、username、password和本机一致。常见做法是直接用MySQL root账号但如果有权限要求记得给账号授予数据库的全部权限。5.2 启动失败时的检查清单现场演示最怕数据库连不上。启动报错时先看日志前三行端口占用、数据库密码、MySQL时区是三个高频问题。端口占用在Windows下用netstat -ano | findstr 8080查找到占用进程后用taskkill /PID 进程号 /F。MySQL时区问题通常报错是“The server time zone value”在数据库连接URL后加serverTimezoneAsia/Shanghai并且useSSLfalse避免因为ssl握手导致连接超时。netstat -ano | findstr 8080 mysql -uroot -p show variables like character_set%;如果页面能打开但登录失败先查t_user表里有没有数据再用注册接口重新创建一个用户。拦截器导致的登录失败和普通SQL错误不一样日志中不会有SQL异常此时可以打开F12看登录请求是否返回302重定向到了/login页面。5.3 答辩演示时让系统“看起来更完整”的三个技巧第一个技巧是提前准备一个“演示用数据库”和“演示用账号”不要用论文截图里的密码。在答辩现场临时录入数据很浪费节奏。我习惯把用户、服务人员、管理员三个账号密码都贴在控制台标签上展示时直接输入。第二个技巧是准备一个状态迁移的测试用例。你可以预先在数据库里把某条订单置为待派单现场演示取消操作让评委看到状态从“0”变成“4”。这么做比只展示列表更接近真实业务也能够引出状态机设计。第三个技巧是把SQL脚本、源码包、论文放在一个统一命名规范的目录里现场演示时打开数据库控制台快速运行一条统计SQL给评委看。比如查询“今天的订单量”要比口头说“系统功能完善”有说服力得多。如果评委追问业务细节你可以直接回到订单状态枚举的代码处把限定流转的if逻辑指给他们看。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价