ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot家政保洁预约系统项目实战:从零到一完整实现

SpringBoot家政保洁预约系统项目实战:从零到一完整实现 每年这个时候总有不少同学在后台私信问我SpringBoot 的毕设项目到底该怎么做才能拿得出手恰好我手头刚完成了一个家政保洁预约系统的完整开发项目代号就叫mwrnnvi8_zl032从数据库设计到权限控制再到前后端联调踩了不少坑也沉淀了不少可以直接复用的经验。这次就把整个项目从零到一的完整思路拆给你看不管是拿来当毕设、课程设计还是自己练手做全栈这篇都够用了。1. 项目整体设计与技术选型1.1 这个项目到底在做什么家政保洁预约系统本质上解决的是一个典型的“服务交易撮合”问题顾客需要保洁服务保洁员/服务商有供给能力系统要做的就是把预约、派单、支付、评价这条链路串起来。从需求视角拆一下这个系统至少包含三类角色用户端注册登录、浏览服务项目、选择时间段、下单预约、在线支付或下单后线下结算、查看订单状态、取消订单、评价服务。管理员端服务项目管理、保洁员管理、订单管理派单/改派/取消、用户管理、数据统计、反馈处理。保洁员端接单提醒、确认接单、完成服务、查看收益记录如果做到财务模块的话。我实际做的时候把重心放在了用户端和管理员端保洁员端用的是一个简化版的 H5 页面逻辑和后台管理端有部分复用。这样做的好处是工作量可控同时三个端都有了答辩时讲演示也不虚。1.2 技术选型为什么是 SpringBoot 这套组合先说结论这套技术栈是这个项目最稳妥的组合没有之一后端Spring Boot 2.7.x MyBatis-Plus Spring Security JWT前端Vue 2 Element UI后台管理端用户端用的则是轻量的 Bootstrap 模板改造数据库MySQL 8.0 Redis会话/缓存开发工具IDEA Navicat Postman Git构建Maven打包方式为 Jar部署到服务器跑 Nginx 反向代理选 SpringBoot 的核心原因一是它足够成熟生态完善面试时也好讲二是它对毕设和中小型项目来说开发效率极高起步快、调试方便不用像 SSM 那样配一堆 XML。注意Spring Boot 3.x 已经出来了但很多第三方组件尤其是一些老牌的分页插件、代码生成器对它的兼容还不太稳定。我这个项目用的是2.7.x 版本JDK 用 1.8或者 11 也行。如果你不是想尝鲜别在版本上给自己挖坑。1.3 架构思路前后端分离但还是“传统”了点这个项目采用了前后端分离结构RESTful API 风格。前端通过 Axios 调用后端接口后端统一返回ResultT结构。{ code: 200, message: success, data: { } }后端内部按四层走Controller → Service → Mapper → Database这不是什么花哨的微服务架构但足够清晰也好扩展。我一直觉得做毕设或者中小型真实项目没必要一上来就拆微服务单体模块化就已经是合理上限了。有一个容易被人忽略的设计点业务层面做了模块切分。用户模块、订单模块、服务项目模块、评价模块、统计模块之间尽量解耦加上头条搜索词里有“springboot项目全局过滤器处理上传pdf文件时xss攻击”这种问题我当时也把全局异常处理和 XSS 过滤统一做了后面会讲。2. 数据库设计与表结构核心逻辑2.1 建表思路六张核心表家政预约系统的表设计并不复杂但如果直接照着业务表一层层堆后期往往会因为字段冗余反反复复改。我最后沉淀下来的核心表如下user用户表含角色字段USER / ADMIN / WORKERservice_category服务分类表日常保洁、深度保洁、开荒保洁、家电清洗等service_item服务项目表挂在分类下面含价格、时长、图片等appointment_order预约订单表核心表order_status_log订单状态变更日志表这个表容易被忽略但做订单管理必备review服务评价表如果你想扩展功能比如积分、优惠券、财务结算在这个基础上加表就可以了不会伤筋动骨。2.2 预约订单表的设计细节订单表是整个系统的“中枢”我列出几个关键字段方便大家直接参考字段名类型说明order_novarchar订单编号生成规则建议“日期随机串”user_idbigint下单用户worker_idbigint派单保洁员service_item_idbigint服务项目service_datedate预约日期service_timeslotvarchar预约时段如 09:00-12:00addressvarchar服务地址statustinyint状态机payment_statustinyint支付状态total_amountdecimal订单金额remarkvarchar备注状态机设计我建议这样定义1-待支付 / 2-待派单 / 3-待服务 / 4-服务中 / 5-已完成 / 6-已取消。这里有个小细节很多人喜欢只在订单表里保留一个 status 字段然后直接 update但这样对“订单被谁在什么时候改过状态”没有记录。我加了order_status_log虽然多写几行代码但调试和答辩时真的能加分。2.3 数据库索引与性能上的几个取舍用户下单查询、管理员后台订单列表最常见的查询条件是status、user_id、service_date所以强烈建议在订单表上建联合索引。我当时建的是ALTER TABLE appointment_order ADD INDEX idx_user_status_date (user_id, status, service_date);索引不是越多越好尤其是在 MySQL 8.0 里。我当时在remark字段上尝试加过全文索引发现对这种小型系统完全用不上反而拖慢写入后来直接删了。如果你的查询条件相对固定就只建那两三个最有价值的联合索引即可。3. 核心功能模块与接口设计3.1 登录鉴权JWT Spring Security 还是“轻量拦截器”很多同学一看到“Spring Security”就头大觉得配置太麻烦。我对这类需求的建议是单纯做毕设JWT 拦截器就可以了如果你希望简历上写 Spring Security就用它来做。这个项目我是用 Spring Security JWT 实现的。核心思路很简单用户登录成功后后端生成一个 token 返回给前端前端存在 localStorage每次请求时放到请求头Authorization里。后端用一个过滤器校验 token 合法性并解析出用户信息放到 SecurityContext 里。关键配置有三块WebSecurityConfigurerAdapter配置放行规则登录、注册、服务列表等接口放行其他接口需要认证。JwtAuthenticationFilter继承OncePerRequestFilter在请求进入 Controller 前完成 token 校验和用户身份组装。密码加密必须用 BCrypt不要用 MD5这一点面试时问到加密时很容易成为加分项。这里踩过一个大坑Spring Security 的过滤器链和 CORS 配置之间偶尔会“打架”如果你遇到“前端请求能到后端但拿不到响应头”的情况检查一下是不是corsConfigurationSource没有注册到 Security 的配置里。3.2 下单预约的核心流程预约下单的流程比较常规但有两个细节要额外注意第一是库存/名额校验。家政项目的“库存”不是具体商品数量而是保洁员在某时段的可服务名额。我当时的做法是在service_item表里增加一个max_orders_per_slot字段下单时用 Redis 的incr记录某保洁员在某时段的订单数超过上限就拒绝下单。虽然 MySQL 也能做但用 Redis 的原子操作更稳并发场景下不容易超卖。第二是订单号生成。直接用数据库自增 id 当订单号很容易暴露业务量而且不太好看。我用的规则是String orderNo JZ LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyyMMddHHmmss)) RandomStringUtils.randomNumeric(4);大概就是你看到的JZ202409151430552847这种结构。后来发现并发高的时候同秒可能出现重复随机串所以加了个“订单号唯一索引重试机制”稳妥一点。3.3 派单逻辑先用人工派单再谈自动派单很多同学在这上面纠结很久到底要不要做自动派单算法我的建议是不要一开始就做复杂的智能派单那是给自己挖坑。这个项目采用“管理员派单为主用户选保洁员为辅”的方式。用户下单时可以选择“指定保洁员”也可以选择“平台指派”。平台指派模式下订单进入“待派单”状态管理员在后台可以查看当前所有订单手动选择保洁员并点击“派单”。为什么不做自动派单因为自动派单需要维护位置距离、实时档期、历史评分、技能匹配等一系列数据逻辑写起来不算难但调试真的很耗时。等基础版本稳定之后我再规划了一个简单的智能派单按“当日订单数最少 评分最高”优先作为扩展功能。4. 安全设计XSS 过滤、统一异常与文件上传4.1 全局 XSS 过滤器细节必须抠搜索词里提到“springboot项目全局过滤器处理上传pdf文件时xss攻击”这个问题在真实开发里确实会遇到——很多人在文本输入框做了 XSS 过滤但上传的文件名、PDF 文件的元数据里照样可以塞恶意脚本。我当时的做法是写了一个XssFilter继承OncePerRequestFilter对请求体里的参数做转义处理重写了HttpServletRequestWrapper的getParameter()、getParameterValues()和getInputStream()。同时针对 PDF 上传场景我在文件上传接口中专门处理了文件名String safeFileName HtmlUtils.htmlEscape(file.getOriginalFilename(), UTF-8);如果文件类型是 PDF还会用 PDFBox 解析元数据把标题、作者等属性里的script等标签剔除掉。这一层保护在日常demo里很少见但真正上线或者参加比赛时会很加分。4.2 统一异常处理另一个项目必备项是全局异常处理器我用RestControllerAdvice实现了三种异常的分层处理业务异常例如余额不足、库存不足返回 code 500 自定义 message。参数校验异常返回 code 400 定位到具体字段名。兜底异常返回 code 500 不包含敏感信息的通用提示。有一点需要特别提醒线上环境不要把异常堆栈直接返回给前端这不仅暴露内部实现细节而且对用户体验也很差。我当时写了一个逻辑如果当前是 dev 环境才在响应里附带堆栈信息如果是 prod只在服务端日志里输出。4.3 文件上传本地存储还是 MinIO这个项目的服务图片、用户头像最初是存到本地目录的但随着项目代码在不同电脑之间迁移路径问题开始频繁冒出来。后来我换成了 MinIO 做对象存储客户端通过预签名 URL 直传不经过后端转存。这样做的好处是后端不扛流量、不占内存文件访问走 MinIO 的地址即可扩展起来也方便。但是有一个细节要特别注意预签名 URL 是有过期时间的如果前端页面停留时间过长图片链接会失效需要在响应时设置一个足够长的有效期例如 7 天或者前端定期刷新 URL。String presignedUrl minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() .bucket(home-service) .object(objectName) .expiry(60 * 60 * 24 * 7) .build());不过这里也要提醒一下本地存储对于毕设来说完全够用MinIO 是为了练手对象存储而加的。如果你时间紧、追求稳定本地存储或七牛云/阿里云 OSS 都可以。5. 前后端联调与典型问题排查5.1 联调之前先把接口文档定好很多同学习惯先写完后端再写前端结果接口字段对不上联调阶段一地鸡毛。我这次用 Apifox 统一管理接口文档后端的实体字段和前端 VO 字段保持一致再生成前端模拟数据。这样一来前端和后端完全可以并行开发。这里有一个实用建议接口返回的数据结构不要直接返回实体类Entity而是返回 VOView Object。因为实体类往往会包含密码、手机号、逻辑删除字段等直接暴露给前端既不安全也容易把前端的取值字段搞乱。我的做法是每个核心模块建一个*.vo包用BeanUtils.copyProperties把 Entity 转成 VO。5.2 联调时的四个高频问题问题一前端拿到的时间串不对MySQL 里的datetime返回给前端时是2025-06-12T09:00:00这种带 T 的格式直接显示非常难看。解决办法是在application.yml里配置spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8问题二跨域请求失败后端开启全局跨域配置后仍然偶尔出现前端报跨域错误。大部分情况是 Spring Security 拦截了 CORS 预检请求OPTIONS需要显式放行http.cors().and().csrf().disable()问题三分页插件失效我之前用 MyBatis-Plus 分页插件经常出现在“第一页正常第二页数据重复”的情况。原因基本是分页插件没有正确注册到 MyBatis 的拦截器链里在配置类中重新注入MybatisPlusInterceptor并添加分页插件即可。问题四Redis 缓存了脏数据用户修改头像或昵称后个人信息接口返回的还是旧值因为缓存没有更新。这类问题没有捷径必须统一封装缓存操作工具类在“更新数据库”时主动删除缓存并保证一套更新的操作同时执行。6. 项目部署与优化扩展6.1 部署方式从本地到云服务器这个项目我最终部署的方式是一台 Linux 云服务器2核4G上面跑 MySQL、Redis、MinIO、Spring Boot Jar 包前端 Vue 打包后的静态文件放在 Nginx 里Nginx 做端口转发和反向代理。关键部署步骤不复杂但有几个坑要提前说不要把application.yml里的配置写死改成读取环境变量。否则每次换环境都要重新打包非常愚蠢。MySQL 账号密码不要用 root/root 上生产至少要单独建一个库账号并限制 IP。服务器上跑 Jar 包时一定要用nohup java -jar xxx.jar logs/out.log 21 这种后台启动方式并用jps -l确认进程是否起来。6.2 Docker 一键编排是否必要如果你想让部署更优雅一点或者想顺便展示一下 Docker 技能那就用 Docker Compose 编排一套。version: 3 services: mysql: ... redis: ... minio: ... backend: build: ./backend ports: 8080:8080 frontend: build: ./frontend ports: 80:80注意Docker 部署 Spring Boot 项目时服务启动顺序不能随便编排因为如果 MySQL 还没初始化好后端启动就会报连接错误。我当时的做法是在启动脚本里加了一个简单的等待机制延时 重试连接比 Docker Compose 原生的depends_on更稳。6.3 还能扩展哪些高价值功能如果你的项目想更进一步从“完成项目”变成“有亮点”下面这几个方向可以选定时任务统计报表每天凌晨统计订单数、营收数据Spring 自带的Scheduled就能搞定。消息通知接入 WebSocket用户下单后实时推送订单状态变化。优惠券系统后台发券、用户领券、下单抵扣。多城市配置服务根据用户定位展示不同城市的服务项目。不过这些功能一定要按“增量”的思路做不要一次性铺开否则很容易把所有东西堆成半成品。我自己的习惯是在核心链路稳定之前绝不碰边缘功能。7. 几个避坑指南与经验总结7.1 通用避坑点项目结构不要乱controller/service/mapper/entity/vo/config 各司其职别把业务逻辑写在 Controller 里这种代码后面根本没法维护。事务注解必须用对下单操作涉及订单表、日志表甚至库存表一定要在 service 方法上加Transactional(rollbackFor Exception.class)默认只回滚RuntimeException这点很多人会忽略。日志别全用 System.out.println至少用Slf4j输出日志调试和排查线上问题时差别巨大。敏感信息不要硬编码数据库密码、JWT 密钥不要写在代码里至少放配置中心或环境变量。7.2 关于答辩和面试的准备做这个项目过程中最有价值的不是把功能做完而是能把每一步的设计理由说出来。面试官如果问“为什么用 Redis 做时段计数”你要是能讲清楚“MySQL 在并发更新下锁竞争严重而 Redis 的原子自增更轻量”这种深度对比就已经超过大部分候选者了。另外有一个高频问题是“订单超时未支付怎么办”。这个项目里我用的方案是用户下单后写一张预支付订单存到 Redis 缓存中并设置过期时间比如 30 分钟Redis 的 key 过期时触发一个监听回调去关单或者放开名额。如果你的版本不想依赖 Redis也可以用定时任务扫表但实时性不如 Redis 方案。类似这种细节平时在写代码时有意积累三五个简历和面试就都有了着落。
返回列表