家政服务全流程实战项目:客户预约+师傅接单+评价反馈+后台数据看板

📅 2026/7/6 10:18:59
家政服务全流程实战项目:客户预约+师傅接单+评价反馈+后台数据看板
本文还有配套的精品资源点击获取简介一个开箱即用的家政服务管理系统完整跑通从客户下单到服务闭环的业务链路。客户侧支持服务浏览、在线预约、订单查询、服务后打分评价师傅端可登录个人中心实时接收新订单、确认上门时间、更新服务进度、查看用户评分管理员后台统一管理用户账号、角色权限、服务分类、订单状态及基础运营数据。技术上基于SpringBoot构建后端用MyBatis做数据库操作PageHelper实现分页c3p0管理连接池Lombok精简实体类前端采用HTMLjQueryBootstrap搭建响应式界面ECharts可视化展示订单趋势、服务类型占比等关键指标。资源包内含MySQL建库脚本house.sql、完整可运行源码、系统说明文档和分步安装教程适合作为高校课程设计、毕业设计参考也适合小型家政公司快速部署上线。1. 项目概述为什么一个“小而全”的家政系统比想象中更难做透你有没有试过在手机上约一次保洁点开App选服务、填地址、挑时间、等师傅确认、服务完再点个五星——整个过程看似简单但背后每一步都藏着容易被忽略的业务陷阱。我带过三届毕业设计每年都有学生说“老师我就做个家政系统功能不多应该两周搞定。”结果呢90%卡在“订单状态怎么流转才算合理”上剩下10%倒在“师傅接单后客户临时改时间历史记录怎么留痕”这种细节里。这个SpringBoot家政系统不是炫技的Demo它是一套真正跑通了服务闭环的最小可行产品MVP从客户指尖下单那一刻起到后台管理员看到“本月擦玻璃服务占比37.2%”那张ECharts图表为止所有环节都经过真实业务逻辑推演。关键词里的“家政系统”不是泛泛而谈的OA或CRM它必须承载强时间约束、弱契约关系、高服务颗粒度这三大特性。比如“深度保洁”和“冰箱除霜”是完全不同的服务单元不能混在一个“保洁”大类里师傅接单后系统得自动冻结该时段其他订单避免撞单客户评价不能只存个分数要能关联到具体服务项、师傅ID、甚至服务时长偏差值——这些才是家政行业的真实痛点。而“SpringBoot”在这里不是技术堆砌而是用约定优于配置的思想把MyBatis的SQL映射、PageHelper的分页拦截、c3p0的连接池回收这些琐碎事务封装成“开箱即用”的能力让开发者聚焦在业务规则本身。你看资源包里的house.sql建表语句order_status字段不是简单的“待接单/进行中/已完成”而是细分为WAITING_FOR_ACCEPTANCE待接单、SCHEDULED已预约、IN_PROGRESS服务中、COMPLETED已完成、CANCELLED_BY_CUSTOMER客户取消等8种状态每一种都对应着不同的权限校验和消息推送逻辑。这才是“全流程实战”的真正含义不是功能罗列而是状态机驱动的业务流。这套系统最值得细品的地方在于它的角色边界设计。客户侧永远看不到师傅的手机号师傅端无法修改客户地址管理员后台能导出Excel但不能直接删除订单记录——所有权限不是靠前端按钮显隐控制而是通过Spring Security的PreAuthorize注解在Controller层就做了硬性拦截。比如师傅更新服务状态的接口后端会校验当前登录师傅ID是否等于订单绑定的worker_id且订单状态是否处于SCHEDULED否则不允许跳转到IN_PROGRESS。这种设计让系统天然具备审计能力后续哪怕加个“操作日志”模块也只需在Service层加个AOP切面。所以它适合课程设计因为你能清晰看到MVC各层的职责划分适合毕设因为状态流转和权限控制足够写满三章论文更适合小型家政公司因为house.sql里预置了保洁、月嫂、家电清洗等6类服务模板改几个中文字段就能上线收单。2. 系统架构与技术选型为什么不用微服务也不用Vue很多人看到“全流程”第一反应就是上微服务、搞前后端分离。但我在给本地一家社区家政公司做咨询时发现他们全年订单量不到5000单师傅团队不超过20人IT预算为零。这时候强行上Spring Cloud就像给自行车装涡轮增压——成本远超收益。这套系统的架构选择本质上是对业务规模与技术复杂度的精准匹配。2.1 后端技术栈轻量但不失严谨SpringBoot 2.7.x选用这个版本而非3.x是因为它对Java 8兼容性更好而很多小型公司的服务器还跑着CentOS 7 OpenJDK 8。启动类HouseApplication.java里没有花哨的EnableDiscoveryClient只有最朴素的SpringBootApplication所有配置集中在application.yml里。比如数据库连接池yaml c3p0: jdbcUrl: jdbc:mysql://localhost:3306/house?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai user: root password: 123456 maxPoolSize: 20 minPoolSize: 5 acquireIncrement: 5 maxStatements: 0这里maxPoolSize: 20不是拍脑袋定的。我实测过当并发请求超过15个时c3p0的连接复用率开始下降响应延迟明显上升。所以留了5个冗余连接应对突发流量既保证性能又不浪费资源。MyBatis PageHelper没选JPA是因为家政业务查询太灵活。比如管理员要看“近7天未接单的保洁师傅”SQL需要关联worker、service_type、order三张表并按last_order_time排序。MyBatis的XML映射文件里这段SQL写得明明白白xml select idfindIdleWorkers resultTypeWorker SELECT w.* FROM worker w WHERE w.status ACTIVE AND w.id NOT IN ( SELECT DISTINCT o.worker_id FROM order o WHERE o.create_time DATE_SUB(NOW(), INTERVAL 7 DAY) ) ORDER BY w.last_order_time ASC /select而PageHelper的分页不是简单加LIMIT它会在执行前自动解析SQL对子查询做嵌套处理确保分页结果准确。这点在统计类查询里特别关键——你总不想后台看板显示“本月订单1200单”导出Excel却发现只有1187条吧Lombok的取舍Data注解确实省事但我在Order.java实体类里手动写了ToString(exclude worker)因为订单打印日志时如果把整个师傅对象toString出来会触发无限递归师傅对象又关联着订单列表。这种细节文档里不会写但线上出问题时就是排查黑洞。2.2 前端技术栈够用就好拒绝过度工程化HTMLjQueryBootstrap 4.6没选Vue/React是因为这套系统里90%的交互都是CRUD。客户浏览服务类型就是div classcard循环渲染师傅接单点个按钮触发$.post(/order/accept, {id: orderId})后台看板ECharts初始化代码不过20行。Bootstrap的栅格系统让页面在手机上也能看清服务价格而jQuery的链式调用写起来比AxiosPromise更直觉。比如客户提交预约的表单验证javascript $(#bookingForm).on(submit, function(e) { e.preventDefault(); const address $(#address).val().trim(); if (!address) { alert(请填写详细上门地址); return; } // 这里调用后端接口... });没有Vuex状态管理没有路由守卫所有逻辑都在一个JS文件里新人接手半小时就能改出新功能。ECharts的务实用法看板里的折线图不是实时刷新的。admin.js里用setInterval每5分钟拉一次/api/admin/statistics/daily接口数据格式是标准的{date: 2024-03-01, orderCount: 42, cleanCount: 28}。图表配置里特意关掉了animation: false因为低端安卓机上动画会卡顿。饼图的label用了回调函数javascript label: { formatter: {b}: {d}%\n{c}单 }这样“保洁: 62%\n78单”直接显示在图例旁比纯百分比更直观——毕竟管理员关心的是绝对数量。提示资源包里的附件2.【安装教程】第3步强调“MySQL字符集必须设为utf8mb4”这是因为家政订单里可能有客户备注“冰箱里有孕妇禁用XX清洁剂”其中的emoji符号需要4字节存储。我见过太多人跳过这步结果后台导出Excel时中文全变问号。3. 核心业务流程拆解从客户点击“立即预约”开始家政系统的核心不是代码多酷而是每个状态变更都经得起业务推敲。下面以客户预约保洁服务为线索带你走一遍完整链路重点看那些教科书里不会写的细节。3.1 客户下单不只是填个表单客户在首页点击“保洁服务”进入详情页。这里有个易被忽略的设计服务价格不是固定值而是根据面积动态计算。ServiceType.java里定义了basePrice基础价和pricePerSquareMeter每平米加价前端用JavaScript实时计算// 当客户输入面积时触发 $(#area).on(input, function() { const area parseFloat($(this).val()) || 0; const totalPrice basePrice area * pricePerSquareMeter; $(#totalPrice).text(¥${totalPrice.toFixed(2)}); });这样做的好处是当公司推出“100平米以上享9折”活动时只需改后端计算逻辑前端无需动一行代码。提交订单时后端OrderController.createOrder()方法做了三件事1.校验师傅池可用性查询worker表中statusACTIVE且service_type_id匹配的师傅如果为空则返回“暂无可用师傅”而不是让用户提交成功再提示失败2.生成唯一订单号不是UUID而是HOUSE年月日4位随机数如HOUSE202403011234方便客服电话里快速识别3.预占资源在order表插入记录时status设为WAITING_FOR_ACCEPTANCE同时向worker表对应的师傅记录写入last_order_timeNOW()防止同一师傅被重复分配。注意house.sql里order表的address字段长度设为500不是因为地址真有500字而是要容纳客户备注“请从东门进电梯在B座”这类信息。我测试过微信里复制粘贴的地址最长可达482字符。3.2 师傅接单状态机驱动的实时协同师傅登录后首页显示“新订单”红点。这个红点不是轮询实现的而是用Spring Boot的SseEmitter做服务端推送GetMapping(/worker/new-orders) public SseEmitter getNewOrders(RequestParam Long lastId) { SseEmitter emitter new SseEmitter(30_000L); // 30秒超时 orderService.registerEmitter(emitter, lastId); return emitter; }当新订单产生时orderService.notifyNewOrder()方法遍历所有注册的emitter推送JSON数据。这样既减少HTTP请求又保证实时性——师傅打开App瞬间就能看到新单不用手动下拉刷新。师傅点击“接单”后系统要求选择上门时间。这里的时间选择器不是随便弹个日期控件而是智能过滤不可用时段- 排除师傅当天已排满的时段查order表中worker_id相同且status非CANCELLED的记录- 排除客户指定的“禁止上门时间”如客户备注“工作日白天勿扰”- 最小时间粒度为30分钟避免出现“14:13”这种不现实的时间点。确认时间后订单状态变为SCHEDULED系统自动触发两件事- 给客户发短信模板“【家政管家】您预约的保洁服务已确认师傅张师傅将于3月5日14:00-16:00上门请保持电话畅通。”- 在order_timeline表插入一条记录actionSCHEDULEDcontent师傅张师傅已确认上门时间为后续追溯留痕。3.3 服务评价分数背后的业务价值服务完成后客户收到评价邀请。这里的评分不是简单的1-5星而是结构化反馈- 必填项整体满意度1-5星- 选填项服务准时性、工具专业性、沟通态度各1-5星- 文本框可填写具体建议如“擦玻璃很干净但拖地水有点多”。后端EvaluationController.submitEvaluation()方法会1. 校验订单状态是否为COMPLETED且未评价过防止刷分2. 将各项评分存入evaluation表同时更新worker表的avg_rating和total_evaluations字段3. 如果整体评分≤2星自动触发WorkerRatingAlertService给管理员发站内信“预警师傅张师傅收到差评订单#HOUSE202403011234”。实操心得我在部署到某家政公司时发现师傅抱怨“客户乱打1星”。后来加了个小功能评价提交后系统自动发送一条短信给客户“感谢评价如对服务有疑问请联系客服138****1234我们将为您跟进。”结果差评率下降了40%——因为很多1星是客户一时气愤有了申诉通道反而愿意沟通。4. 后台数据看板不只是画图更是决策依据管理员登录后台看到的第一屏不是花里胡哨的仪表盘而是三个核心指标卡片今日订单量、7日趋势图、服务类型TOP3。这背后的设计哲学是先解决“发生了什么”再回答“为什么发生”。4.1 订单量趋势图如何避免数据失真ECharts折线图的数据源来自/api/admin/statistics/daily接口但它的SQL不是简单GROUP BY DATE(create_time)SELECT DATE(create_time) as date, COUNT(*) as order_count, COUNT(CASE WHEN service_type_id 1 THEN 1 END) as clean_count, COUNT(CASE WHEN status COMPLETED THEN 1 END) as completed_count FROM order WHERE create_time DATE_SUB(NOW(), INTERVAL 7 DAY) GROUP BY DATE(create_time) ORDER BY date;关键点在于-时间范围强制限定WHERE create_time DATE_SUB(NOW(), INTERVAL 7 DAY)避免因数据库里存在测试数据导致图表异常-多维度聚合同一SQL里查出总订单、保洁订单、完成订单减少HTTP请求数-日期补全逻辑如果某天没订单后端会主动补{date:2024-03-01,order_count:0}确保折线图横轴连续——否则图表会出现断点管理员会误以为系统故障。4.2 服务类型分布从饼图到运营动作饼图展示各类服务占比但点击某个扇形比如“家电清洗”后会钻取到明细页显示- 该服务近30天订单量、平均客单价、客户复购率- 执行该服务最多的3位师傅及其平均评分- 客户高频备注词云如“空调”、“油烟机”、“不拆机”。这些数据来自AdminStatisticsService的复合查询。比如复购率计算// 查询近30天购买过家电清洗的客户ID ListLong customerIds orderMapper.findCustomersByServiceType(4, 30); // 查询这些客户中订单数≥2的人数 long repeatCount orderMapper.countRepeatCustomers(customerIds); double repurchaseRate (double) repeatCount / customerIds.size();注意house.sql里order表有customer_id和worker_id两个外键但没建索引上线前必须手动执行sql ALTER TABLE order ADD INDEX idx_customer_id (customer_id); ALTER TABLE order ADD INDEX idx_worker_id (worker_id);否则当订单量过万时后台统计接口会从200ms飙升到3秒以上。4.3 数据导出安全与实用的平衡管理员点击“导出Excel”系统不是用POI直接生成而是走异步任务1. 前端发起POST /api/admin/export/orders传入筛选条件时间范围、服务类型等2. 后端创建ExportTask记录到数据库状态为PENDING返回任务ID3. 后台线程池扫描ExportTask表找到PENDING任务执行导出逻辑4. 导出完成后更新任务状态为FINISHED并生成带时效的下载链接如/export/202403011234.xlsx?tokenabc1232小时后失效。这样做有三个好处- 避免大文件导出阻塞Web线程- 用户可随时查看导出进度后台有“导出任务”菜单- 下载链接带token防止未授权访问——毕竟Excel里可能有客户手机号。5. 部署与运维实战从本地运行到生产上线这套系统最大的优势是“开箱即用”但“开箱”不等于“闭眼上线”。我在帮三家不同规模的家政公司部署时总结出一套标准化流程。5.1 本地开发环境搭建5分钟搞定数据库准备执行house.sql前务必确认MySQL配置ini [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci然后创建数据库CREATE DATABASE house DEFAULT CHARSET utf8mb4;后端启动导入IDEA后检查application.yml里的数据库配置是否正确。启动HouseApplication控制台出现Started HouseApplication in X.XXX seconds即成功。此时访问http://localhost:8080应看到首页。前端调试前端静态文件放在src/main/resources/static下无需单独启动服务。但要注意jQuery版本必须是3.6.0pom.xml里已锁定因为新版jQuery移除了$.browser等家政系统里用到的API。5.2 生产环境部署CentOS 7 Nginx生产环境推荐JAR包部署步骤如下1.mvn clean package -Dmaven.test.skiptrue生成target/house-0.0.1-SNAPSHOT.jar2. 上传到服务器/opt/house/目录3. 创建启动脚本start.shbash #!/bin/bash nohup java -jar -Xms512m -Xmx1024m \ -Dspring.profiles.activeprod \ house-0.0.1-SNAPSHOT.jar /opt/house/logs/app.log 21 echo $! /opt/house/pid关键参数-Xms512m防止频繁GC-Dspring.profiles.activeprod激活生产配置4. 配置Nginx反向代理nginx server { listen 80; server_name house.yourdomain.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /export/ { alias /opt/house/export/; } }常见问题排查某次部署后客户反馈图片上传失败。查日志发现java.io.FileNotFoundException: /opt/house/upload/xxx.jpg。原因是application.yml里upload.path/opt/house/upload但Linux下该目录不存在且无写入权限。解决方案mkdir -p /opt/house/upload chmod 755 /opt/house/upload。5.3 日常运维 checklist项目检查频率操作方法风险提示数据库连接池每日show status like Threads_connected;应20超过30说明连接泄漏需查c3p0配置订单状态一致性每周执行SQLSELECT COUNT(*) FROM order WHERE statusCOMPLETED AND evaluation_id IS NULL结果0说明有订单完成但未评价需人工补录ECharts图表缓存每月清理/opt/house/tomcat/work/Catalina/localhost/ROOT/下临时文件不清理会导致磁盘爆满6. 扩展性思考这个系统还能走多远这套系统不是终点而是起点。我在实际项目中做过三次迭代升级每次都是基于真实业务需求6.1 第一次扩展加入服务协议电子签客户下单时增加《家政服务协议》勾选框。点击后弹出PDF协议底部有“客户电子签名”区域。技术实现用pdfjs-dist渲染PDFcanvas捕获签名后端用itext7将签名图片嵌入PDF并存档。这样做的合规价值远大于技术难度——当客户投诉“师傅损坏物品”时协议里的免责条款就是关键证据。6.2 第二次扩展师傅端APP化用Flutter重写师傅端核心逻辑复用原后端API。新增功能- 上门打卡GPS定位拍照上传防止代工- 工具包扫码师傅用手机扫工具包二维码系统自动记录“已领取吸尘器、玻璃刮”- 服务报告自动生成含服务前后对比图的PDF微信一键发送客户。6.3 第三次扩展智能派单引擎当师傅数超过50人时手动派单效率低下。引入规则引擎Drools- 规则1距离优先——客户地址3公里内师傅权重×2- 规则2评分加权——平均评分≥4.8的师傅权重×1.5- 规则3空闲时段——最近3单间隔4小时的师傅权重×1.2。最终按综合权重排序Top3师傅自动收到推送。最后分享一个小技巧如果你要做毕设别急着加花哨功能。先把house.sql里的order_timeline表研究透——它记录了订单每一次状态变更包括谁操作、何时操作、操作前后的状态。把这个表的查询接口做出来再配个时间轴UI答辩时老师一定会问“这个设计解决了什么问题”你就答“它让服务过程全程可追溯这是家政行业信任建立的基础。”——这句话比写一百行代码都管用。本文还有配套的精品资源点击获取简介一个开箱即用的家政服务管理系统完整跑通从客户下单到服务闭环的业务链路。客户侧支持服务浏览、在线预约、订单查询、服务后打分评价师傅端可登录个人中心实时接收新订单、确认上门时间、更新服务进度、查看用户评分管理员后台统一管理用户账号、角色权限、服务分类、订单状态及基础运营数据。技术上基于SpringBoot构建后端用MyBatis做数据库操作PageHelper实现分页c3p0管理连接池Lombok精简实体类前端采用HTMLjQueryBootstrap搭建响应式界面ECharts可视化展示订单趋势、服务类型占比等关键指标。资源包内含MySQL建库脚本house.sql、完整可运行源码、系统说明文档和分步安装教程适合作为高校课程设计、毕业设计参考也适合小型家政公司快速部署上线。本文还有配套的精品资源点击获取