SpringBoot家政服务管理平台毕设:表设计、状态机与权限实战

发布时间:2026/9/11 16:44:47
SpringBoot家政服务管理平台毕设:表设计、状态机与权限实战 简介面向Java毕业设计与课程设计场景这份SpringBoot家政服务管理平台源码包含前台操作与后台管理两部分前台提供首页、服务信息、公告、留言反馈与个人中心后台涵盖用户、服务人员、服务类型、预约、取消、分配、进度及评价等管理功能适合需要完成相似课题或想学习SpringBoot前后端整合开发的学生参考。压缩包约75.21MB主要包含项目源代码、LW文档、PPT演示文稿与视频演示既能用于代码阅读和二次开发也可支撑论文撰写与答辩展示。目前已有313人学习下载项目基于JDK1.8、SpringBoot、MySQL5.7与Tomcat7导入后可正常运行。借助这份资源可以快速理解家政服务平台的业务流程与角色权限设计掌握前后台数据交互的逻辑并借鉴其目录结构与代码组织方式加以扩展能有效节省从零搭建项目的时间是一份结构完整的毕业设计参考方案。1. 用 springboot 做家政服务管理平台毕设选题为什么最稳每年毕设里最不缺的就是“xx管理系统”这类题目家政服务管理平台看着平淡却正好把 springboot 项目源码里最该展示的几块都覆盖了用户角色、服务项目、预约派单、评价支付、权限控制和经营统计。它不追高并发也不依赖复杂算法核心是讲清楚“一个真实系统怎么从 ER 图落到可运行接口”。适合拿来做 Java 毕业设计、课程设计也适合刚学完 SSM 的人用 springboot 重写一套完整项目练手。我按平时搭这类项目的顺序往下讲先拆业务和表再搭 springboot 工程然后实现预约派单、权限和统计最后落到演示数据和答辩资料。2. 家政服务管理平台的业务边界与核心表拆分2.1 三个角色和一条主流程家政平台的范围如果一开始不控制后期会做成“大杂烩”。常见做法是只围绕一条主流程客户选服务项目、下单、支付管理员看到“待派单”订单后指派服务人员服务人员接单并按预约时间上门服务完成客户确认并评价。这条流程里最核心的实体不是“服务人员”而是“服务订单”。订单把用户、服务项目、服务人员、金额和状态串在一起评价格、支付流水、派单记录都以它为中心。三个角色对应的权限边界也要先定清楚否则接口会越写越乱客户注册登录、浏览项目、创建订单、支付、取消订单、评价。服务人员查看派给自己的订单、接单、提交完成状态。管理员维护服务项目和服务人员、审核订单、派单、查看经营统计发布公告。这些用例不需要做成几十个独立页面。采用 springboot Vue 前后端分离时后端接口按/user/**、/worker/**、/admin/**分组权限只要在 Security 配置里按路径切一刀大部分角色控制就完成了。2.2 核心业务表能讲清楚 7 张就够表设计上常见方案是用户表 user、服务项目表 service_item、服务订单表 service_order、派单记录表 dispatch_record、评价表 evaluation、公告表 notice、支付流水表 payment。如果题目里没有明确要求积分、会员等级这类表不建也没关系硬加反而会把 ER 图撑得很难讲。服务项目表字段不必多id、name、category、price、cover_url、description、status。价格用DECIMAL(10,2)不要用 float否则金额计算会出现精度问题。用户表用 role 字段区分身份0 客户1 服务人员2 管理员。服务人员额外有联系电话、服务区域、接单状态。订单表是整个平台的晴雨表字段设计直接决定后面查询好不好写。表结构大致如下2.3 订单表的 DDL 与字段说明CREATE TABLE service_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单号, user_id BIGINT NOT NULL COMMENT 下单用户, service_item_id BIGINT NOT NULL COMMENT 服务项目, worker_id BIGINT DEFAULT NULL COMMENT 服务人员, appoint_time DATETIME NOT NULL COMMENT 预约时间, address VARCHAR(255) NOT NULL COMMENT 服务地址, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待支付 1待派单 2待服务 3服务中 4已完成 5已取消, pay_amount DECIMAL(10,2) NOT NULL COMMENT 实付金额, remark VARCHAR(255) DEFAULT NULL, create_time DATETIME NOT NULL, update_time DATETIME NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这份 DDL 已经能支撑大多数查询。order_no 用来展示和核对最好由后端生成不要暴露自增主键worker_id 允许为空因为刚创建的订单还没派单status 用数字存储在 Java 代码里用枚举维护避免把 0、1、2 散落在业务代码里。注意create_time 和 update_time 建议在 MyBatis-Plus 里用数据库时间或自动填充统一处理不要在每条 insert 里手动 set否则很容易出现“忘了更新时间”这类小毛病。用户和订单是一对多订单和服务项目是多对一订单和派单记录是一对多。只要以 service_order 为中间核心外键关系就不会乱。这个平台的业务没有多对多场景不用画用户-服务人员这种关联表画多了反而会被追问“为什么要这么设计”。3. 用 springboot 搭项目骨架依赖、YAML 配置和统一返回3.1 技术选型springboot 版本怎么定家政服务管理平台这个规模技术组合不要求新。选 springboot 2.7.18 JDK 8 MyBatis-Plus 3.5.x MySQL 8 Redis比直接用 springboot 3.x 更适合毕设。原因有三个机房和家用电脑的 Java 8 环境最好配网上能查到的 springboot 配置笔记、面试题大部分基于 2.xspringboot 版本太高之后javax 包名变成 jakarta部分 starter 的行为也变了最后消磨时间的全是环境问题而不是业务问题。如果用 springboot 3.x就必须配 Java 17 和 jakarta 命名空间。除非项目要求新不建议在毕设里背这个包袱。3.2 项目目录结构后端工程按下面结构组织Controller、Service、Mapper 分层要清晰home-service/ ├── pom.xml └── src/main/ ├── java/com/example/homeservice/ │ ├── common/ # 统一返回 R、业务异常 │ ├── config/ # Security、Redis、MyBatis-Plus 配置 │ ├── controller/ # 接口层 │ ├── service/ # 业务层 │ ├── mapper/ # MyBatis-Plus Mapper │ ├── entity/ # 实体类 │ └── dto/ # 入参和出参 └── resources/ ├── application.yml └── mapper/ # 复杂 SQL 的 XML 文件Controller 里不要直接接收 Entity而是接收 DTO避免前端把不该传的 id 或 role 一起交到后端。Service 里加事务多个表操作不会出现只写一半的问题。Mapper 层继承 MyBatis-Plus 的 BaseMapper单表 CRUD 基本不写 SQL。3.3 pom.xml 关键依赖parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency /dependencies这里没写 JWT 和 Jasypt 的依赖等用到时按对应版本补。springboot 2.7.18 里的 mysql-connector-java 用的是旧坐标如果换成 springboot 3.x坐标要变成com.mysql:mysql-connector-j这也是版本太高带来的常见问题之一。3.4 application.yml 配置密钥写进 yml 密文数据源、Redis、MyBatis-Plus 的配置集中在 application.yml 里。数据库密码不要明文放在作业包里常见做法是用 Jasypt 对密码加密配置里写ENC(密文)。spring: datasource: url: jdbc:mysql://localhost:3306/home_service?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: ENC(xxxxxx) driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 database: 0 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: map-underscore-to-camel-case: true jasypt: encryptor: password: ${JASYPT_PASSWORD:home-service}JASYPT_PASSWORD是加解密用的主密码不写死在代码里后面的:home-service是兜底值方便本地测试。配置里的map-underscore-to-camel-case打开后数据库的下划线字段能自动映射到实体的驼峰属性否则create_time映射不到createTime查询结果全是 null。3.5 统一返回结果和全局异常接口返回格式统一前端和答辩演示都省事。我自己会先写一个 R 类public class RT { private Integer code; private String message; private T data; public static T RT ok(T data) { RT r new R(); r.code 200; r.message 操作成功; r.data data; return r; } public static T RT fail(String msg) { RT r new R(); r.code 500; r.message msg; return r; } }再配一个 RestControllerAdvice 全局异常处理器。业务代码里直接throw new BizException(订单不存在)异常处理器统一转成R.fail(message)。这样 Service 层不用处处写 try-catch前端拿到的永远是{code, message, data}这种结构而不是一堆堆栈信息。4. 预约派单模块用状态机管住订单流转4.1 从 status 字段到状态机订单表里的 status 看起来只是一个数字但它背后就是状态机。家政平台的订单状态定义成六个就够0 待支付、1 待派单、2 待服务、3 服务中、4 已完成、5 已取消。当前状态触发动作下一状态待支付用户支付成功待派单待支付用户未支付且超时已取消待派单管理员指派服务人员待服务待服务服务人员开始服务服务中服务中服务人员提交完成已完成只在状态机允许的路径里做变更比在 Service 里写一堆 if 判断要清楚很多。尤其“已取消”这个状态不是任何状态都能转过去待派单之后只有管理员取消用户不能直接把自己订单改成取消否则记录就乱了。先定义一个枚举public enum OrderStatus { WAIT_PAY(0, 待支付), WAIT_DISPATCH(1, 待派单), WAIT_SERVICE(2, 待服务), SERVING(3, 服务中), FINISHED(4, 已完成), CANCELED(5, 已取消); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code code; this.desc desc; } public int getCode() { return code; } public String getDesc() { return desc; } }4.2 下单接口的实现下单接口要做的不是一行 insert而是先做校验再生成订单号最后落库。下面这段是核心逻辑Transactional(rollbackFor Exception.class) Override public Long createOrder(CreateOrderDTO dto) { User user userService.getById(dto.getUserId()); if (user null) { throw new BizException(用户不存在); } ServiceItem item serviceItemService.getById(dto.getItemId()); if (item null || item.getStatus() ! 1) { throw new BizException(服务项目不存在或已下架); } ServiceOrder order new ServiceOrder(); order.setOrderNo(generateOrderNo()); order.setUserId(user.getId()); order.setServiceItemId(item.getId()); order.setPayAmount(item.getPrice()); order.setAddress(dto.getAddress()); order.setAppointTime(dto.getAppointTime()); order.setStatus(OrderStatus.WAIT_PAY.getCode()); orderService.save(order); return order.getId(); }Transactional保证订单写入过程中如果出现异常已经执行的 SQL 全部回滚。这里没有在前端传金额而是后端根据服务项目表重新取一次价格避免用户改请求金额。DTO 里应该只允许传 userId、itemId、address、appointTime不要把 price 放到入参里。订单号生成使用 Redis 自增比数据库表查询要快也方便展示private String generateOrderNo() { String date LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE); Long seq redisTemplate.opsForValue().increment(order:seq: date); return HS date String.format(%06d, seq); }当天的订单号从 HS20260707000001 开始增长。这里要注意 Redis 的 key 不是order:seq而是带日期前缀否则跨天以后订单号不会归零。4.3 管理员派单状态校验是关键派单是管理员操作接口放在/admin路径下。业务逻辑必须校验两件事订单当前状态是不是待派单被指派的用户是不是服务人员。Transactional(rollbackFor Exception.class) public void dispatch(Long orderId, Long workerId) { ServiceOrder order orderService.getById(orderId); if (order null) { throw new BizException(订单不存在); } if (order.getStatus() ! OrderStatus.WAIT_DISPATCH.getCode()) { throw new BizException(只有待派单的订单才能派单); } User worker userService.getById(workerId); if (worker null || worker.getRole() ! 1) { throw new BizException(请选择有效的服务人员); } order.setWorkerId(workerId); order.setStatus(OrderStatus.WAIT_SERVICE.getCode()); orderService.updateById(order); }worker.getRole() ! 1这里约定 1 是服务人员角色实际项目里最好用角色常量类不要散落魔法数字。派单之后还要做一件事给服务人员发一条消息推送。如果没接入消息中间件可以在派单记录表里插一条数据服务人员端“待接单”列表直接查这张表。4.4 Redis 自增报错increment 不是 integer or out of range用redisTemplate.opsForValue().increment(order:seq:20260707)时最容易遇到的报错是ERR value is not an integer or out of range。原因通常是这个 key 之前存过非数字内容比如在调试时往同一个 key 里 set 过字符串。排查命令redis-cli GET order:seq:20260707如果返回值不是纯数字说明 key 被污染了。用 DEL 删掉再重新下单即可redis-cli DEL order:seq:20260707另外要确认 RedisTemplate 的 key 和 value 序列化器。默认 JdkSerializationRedisSerializer 会把值序列化成二进制increment 之前会先反序列化反而更容易出问题。常见做法是给 stringRedisTemplate 单独配置 StringRedisSerializer或者直接用 Spring 提供的 StringRedisTemplate 处理这种纯数字用途。5. 权限控制和统计报表让 springboot 项目更像真实系统5.1 用 Spring Security JWT 做登录鉴权家政平台有客户、服务人员、管理员三种身份不能只靠前端隐藏按钮控制权限。后端要在每次请求里解析出当前用户再判断他能不能调这个接口。常见做法是 Spring Security JWT。登录成功后给用户签发一个 JWT用户后续请求在 Header 里带Authorization: Bearer token。后端用一个过滤器拦截请求、解析 token、把用户信息放进 SecurityContext。核心配置大致如下Configuration EnableWebSecurity EnableGlobalMethodSecurity(prePostEnabled true) public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/login, /register).permitAll() .antMatchers(/admin/**).hasRole(ADMIN) .antMatchers(/worker/**).hasRole(WORKER) .anyRequest().authenticated() .and() .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); } }这段配置在 springboot 2.7.x 下可以直接用。/admin/**只允许 ADMIN 角色访问/worker/**只允许 WORKER 角色访问。注意 hasRole 会自动给角色加ROLE_前缀签发 token 时给用户的权限字段要写成ROLE_ADMIN这种格式否则鉴权永远不通过。5.2 接口级权限PreAuthorize 的用法路径权限适合粗粒度控制接口级控制建议用PreAuthorize。哪怕路径是/admin/dispatch也能在方法上再加一道RestController RequestMapping(/admin) public class AdminOrderController { PostMapping(/dispatch) PreAuthorize(hasRole(ADMIN)) public RVoid dispatch(RequestBody DispatchDTO dto) { orderService.dispatch(dto.getOrderId(), dto.getWorkerId()); return R.ok(null); } }这种写法在答辩时很容易讲第一层是路径访问控制第二层是方法权限校验。如果只想让用户查自己的订单则不要用 hasRole而是在 Service 里判断order.getUserId()是否等于当前登录用户的 idLong currentUserId SecurityUtil.getCurrentUserId(); if (!order.getUserId().equals(currentUserId)) { throw new BizException(无权查看该订单); }5.3 经营统计一条 SQL 做完日报管理员端最常见的统计是未来 30 天每天接了多少单、营收多少。用 service_order 单表聚合就可以完成SELECT DATE(create_time) AS day, COUNT(*) AS order_count, SUM(pay_amount) AS turnover FROM service_order WHERE status 4 GROUP BY DATE(create_time) ORDER BY day DESC LIMIT 30;统计里最容易犯的错是把“已完成”之外的状态也算进收入。status 4 表示已完成未支付、已取消、服务中的订单都不应该计入营收。DATE(create_time) 把时间截到天GROUP BY 之后再排序。后端接收 SUM 的结果时用 BigDecimal金额字段不要用 Double。5.4 heapdump 敏感信息泄露漏洞springboot 项目如果引入了 actuator 并把 heapdump 端点暴露到公网攻击者下载内存快照后可能翻到数据库密码、Redis 密码、JWT 密钥。毕设答辩时如果被问安全加固第一件事就是关闭不需要的端点。management: endpoints: web: exposure: include: health,info这样只会暴露健康检查和基本信息。如果要保留 heapdump 做线上排查必须加 IP 白名单或放到内网不要跟着业务服务一起暴露到公网。springboot heapdump 敏感信息泄露漏洞不是新概念但很多教程默认全开这里值得单独写一句。6. 拿到 springboot 家政平台源码后运行顺序、演示数据和答辩技巧6.1 本地跑通的顺序先别直接点击启动类按顺序做三件事用 Navicat 或命令行执行项目里的 SQL 脚本把表和演示数据建好确认本地 Redis 已启动redis-cli ping能返回 PONG检查 application.yml 里的数据库账号密码。如果 springboot 版本太高导致项目起不来优先改回 2.7.x 并刷新 Maven 依赖再处理 jakarta 相关报错。6.2 演示数据要准备成一条链路演示时最怕临时录入。提前准备一个管理员账号、一个服务人员账号、一个客户账号、三个服务项目、五条不同状态的订单。演示路径固定为“用户注册 → 下单 → 管理员派单 → 服务人员接单 → 完成 → 评价”走完正好覆盖 6 张核心表。6.3 PPT 与 LW 的章节对照PPT 不需要把每个接口都放上去而是按 LW 的结构走背景意义、系统需求分析、ER 图和功能模块图、核心表设计、核心功能演示、总结。视频录制时按上面那条演示链路操作画面里要有数据库和 Redis 的窗口比单纯录网页更有说服力。答辩被追问“订单状态为什么要用枚举”“Transactional 放在哪个方法上”时直接把状态机转移图画出来比背概念更能说明你真正写过这套 springboot 家政服务管理平台代码。答不出来也不要慌明确地说“这里我只在 Service 层做了 try-catch没有处理并发重复提交”然后接一句“如果上线我会在订单表加 version 字段做乐观锁”反而能体现边界感。本文还有配套的精品资源点击获取