“家政服务管理系统”这个项目是我在完整走了一遍之后还想反复回看的一套Spring Boot Vue前后端分离项目。第一次做的时候我只是照着源码把功能跑通第二次才真正看明白每个表为什么这么建、订单状态为什么要定成那几类、派单功能为什么不能单纯靠人工。如果你正准备做一个类似的管理系统或者正被课程设计、外包项目卡在“功能好写、业务难缠”这一步这篇文章应该能给你不少实操层面的启发。我会从业务拆解、技术选型、订单状态机、数据库设计、前端权限、部署上线这几个角度把Spring Boot Vue家政服务管理系统从零到上线的完整链路梳理一遍。和普通教程最大的区别是我会把那些“代码里没写、文档里没提、只有真正踩过坑才知道”的细节一并讲清楚。源码和数据库脚本都是可以对应着看的建议你手边放一份代码边看边翻。1. 家政服务系统的业务体检先搞清楚要管什么很多人在拿到“家政服务管理系统”这个题目之后第一反应是打开编辑器直接建表。这其实是最容易翻车的做法因为家政服务和普通电商系统有一个本质区别电商管的是“货”家政管的是“人”。货不会累、不会迟到、不会对服务结果有主观感受但家政阿姨会。所以系统的核心不是商品和库存而是服务人员的时间、技能、状态以及一次上门服务从下单到完结的全过程。1.1 线下管理模式的几个痛点先说说传统家政公司是怎么运作的。用户打电话或者到店下单客服记在本子上再翻阿姨的排班本子找空闲的人打电话确认然后告诉用户“明天下午两点阿姨上门”。整个过程极度依赖人的经验和责任心一旦单量上来问题就跟着来同一个阿姨被重复预约客服之间信息不同步订单撞车。用户问“我家那个阿姨什么时候到”客服得翻本子才能回答。阿姨临时请假没人知道哪些订单受影响。用户投诉服务不满意口说无凭连服务记录都拿不出来。这些痛点落到系统里就变成了几个核心需求阿姨的日程必须可控可查订单的状态必须可追踪服务记录和评价必须留痕。所以我在设计的时候没有把“订单表”当成唯一的中心而是把“阿姨的时间”当成和订单同等重要的资源来管理。1.2 角色划分与核心业务链路家政服务管理系统通常分三类角色前台用户、后台管理员/客服、服务阿姨。有的系统还会把管理员和客服拆开但在中小型项目里合并成“管理员”一类角色就够了。主流程是这样的用户注册登录选择服务项目比如日常保洁、深度清洁、家电清洗选择预计服务日期和时间段提交订单并支付定金或全款。管理员在后台看到待派单的订单根据服务类型和用户所在区域选择空闲阿姨手动或由系统推荐派单。阿姨在手机端或小程序端收到派单通知确认接单按约上门。服务完成后用户确认验收、付款并对服务进行评价。如果出现售后投诉管理员需要能查到完整的订单轨迹。这套链路拆清楚之后系统的模块边界也就出来了用户模块、服务项目管理、订单模块、阿姨管理模块、评价售后模块再加上后台的权限管理和数据统计。每个模块不要急着写代码先把字段和状态列出来做到自己心里有数后面开发才不会反复返工。2. 技术选型复盘Spring Boot Vue 这套组合好在哪儿技术选型这件事我见过太多人一上来就问“哪个框架最牛”实际上比框架更重要的是你自己的维护能力和团队的配合习惯。Spring Boot Vue这套组合在国内中小型项目里能这么流行不是没有道理的。2.1 Spring Boot 后端的选择逻辑后端用Spring Boot首先是因为Java生态本身够稳。家政服务系统的核心诉求是“事务正确”尤其是订单支付、派单、取消这些操作一旦出问题就会引发客诉。Spring Boot自带的声明式事务管理加上成熟的连接池和ORM框架能让团队把精力放在业务逻辑上而不是花大量时间处理底层基础设施。再有一个很实际的原因招人容易、资料多。Spring Boot的文档和现成组件几乎是全行业最全的随便搜一个“订单功能怎么实现”都能找到参考。对独立开发者或者小团队来说遇到问题能快速找到解决方案有时候比技术本身更前沿还要重要。我在这套系统里用的是Spring Boot 2.x MyBatis-Plus MySQL Redis。MyBatis-Plus负责单表CRUD和条件构造Redis做缓存、登录token存储和一些计数类的统计。为什么加Redis因为系统里有一个高频操作用户查询阿姨的档期。每次查询都要join订单表、阿姨表、服务项目表如果不加缓存数据库压力会很明显。后面会讲具体怎么用的。2.2 Vue 前端与工程协作方式前端选Vue的原因也很直白上手快、组件生态丰富、能很好地做前后端分离。我这次用的是Vue 3 Element Plus Axios Vue Router Pinia如果你对Vue 2更熟这套逻辑同样适用核心差别只在语法上。前后端分离最大的收益是职责清晰。后端只出JSON接口和数据表前端负责页面渲染和交互。两边约定好接口格式各干各的不用像传统模板引擎那样把Java代码和HTML混在一起改。这个项目里我定了统一的响应结构不管成功失败都返回{ code: 200, message: 操作成功, data: ... }这样的格式前端拿到code再决定走什么分支。这个小约定能省掉后面至少一半的联调时间。2.3 选型阶段差点妥协的地方说实话中途有同事建议用更轻的Node.js或Flask理由是开发快。但后来复盘发现家政系统的业务复杂度其实不低订单状态流转、阿姨日程冲突校验、售后流程这些东西用强类型语言写起来更难出错。而且项目要交付给客户看Java那一套部署规范虽然繁琐但长期维护起来反而省心。选型这件事一定要从“谁长期维护、谁接手写代码”的角度出发而不是只看前两周的编码速度。3. 订单状态机与派单逻辑全项目最核心的代码家政服务管理系统里订单模块的代码量不是最大的但复杂度最高。因为订单不是一个静态记录它是一条不断变化的状态流而且每个状态都对应不同的用户操作、后台操作和阿姨操作。状态设计得好后面的代码都是往里填逻辑状态设计得乱后面越写越痛苦最后连“这个订单到底处于什么阶段”都说不清。3.1 订单状态为什么必须“穷举”我的建议是开发之前先写一个状态枚举类把所有可能的状态一次性列出来宁可多列、不要漏。我给这套系统定的状态是这样的public enum OrderStatus { PENDING_PAY(0, 待支付), PENDING_DISPATCH(1, 待派单), PENDING_SERVICE(2, 待服务), IN_SERVICE(3, 服务中), PENDING_CONFIRM(4, 待用户确认), COMPLETED(5, 已完成), CANCELED(6, 已取消), REFUNDED(7, 已退款); private final Integer code; private final String desc; OrderStatus(Integer code, String desc) { this.code code; this.desc desc; } // getter... }为什么要穷举因为业务操作都是围绕状态判断展开的。用户只能取消“待支付”和“待派单”的订单管理员只能派“待派单”的单阿姨只能把“待服务”改成“服务中”。如果状态不是明确定义的枚举而是随便在数据库里填字符串那每次业务判断都得写一堆模糊的条件代码根本维护不了。更要留意的是状态的“单向性”。订单状态整体是沿着流程往下走的不应该允许用户把一个“已完成”的订单改回“待支付”。所以我在更新订单状态时会先校验当前状态是否符合变更条件不符合就直接抛出业务异常。这一步能挡住很多奇怪的脏数据也是后面数据统计不出错的保证。3.2 派单的两种实现手动派单与系统推荐派单是家政系统里最“家政特色”的功能。电商系统没有这个环节订单付款后直接进仓库但家政系统必须人工或半自动地决定“这个阿姨去做这个订单”。我在系统里做了两种方式。第一种是纯手动派单管理员在订单详情页筛选空闲阿姨点击“派单”并填写备注订单状态从“待派单”变成“待服务”同时给阿姨推送一条通知。第二种是系统推荐管理员可以不选阿姨直接点“智能推荐”后端根据服务类型匹配、阿姨评分、历史接单次数、当天空闲时间四个维度打分返回推荐列表管理员再确认。推荐算法的打分逻辑可以很简单不一定要上什么复杂模型// 简化版的推荐计分满分100 int score 0; score worker.getScore() * 20; // 评分占比20% score serviceTypeMatch ? 30 : 0; // 服务类型匹配占比30% score 50 - Math.min(50, todayOrderCount * 5); // 当天接单越少分越高推荐列表出来之后管理员仍有最终决定权这样既提高了效率又保留了对特殊情况的处理空间。比如用户指定要某个阿姨那就不走推荐系统要有“指定阿姨”的入口。故宫的门锁得再有面子钥匙还是要两把一个道理。3.3 服务时间冲突校验派单功能最容易出bug的就是时间冲突同一个阿姨在同一个时间段被派给了两个用户。这个问题的根子在于订单表里只存了一个“预约时间”字段没有考虑服务时长。比如用户下单时选了“下午2点”但保洁服务可能要干3个小时那阿姨在2点到5点之间就是不可用的。我的做法是下单选“预计开始时间”和“服务时长”结合得到预计结束时间。派单之前先查一下冲突SQL大概是这样的SELECT COUNT(*) FROM service_order WHERE worker_id #{workerId} AND status IN (2, 3) AND start_time #{endTime} AND end_time #{startTime}这里用了一个比较巧妙的区间重叠判断。判断两个区间是否重叠不需要分别比较开始和结束只需要满足“我开始的比你结束的早”且“我结束的比你开始的晚”就说明有交集。这个写法几乎成了我所有日程类功能的标准写法包括后面做会议室预约系统也沿用同样的思路。有了这段校验派单接口就不会把同一个阿姨重复预约了。但要注意一点这个校验必须在数据库层面也做约束不能只靠Java逻辑。我的习惯是给订单表加一个唯一索引字段组合是worker_id start_time这样哪怕代码漏了校验或者两个请求并发进来数据库也会拦截。对付并发问题永远要多加一层保险。4. 数据库设计里那些容易返工的地方家政服务系统的表结构看起来不复杂但真到了设计细节还是有不少容易返工的地方。拿订单表和阿姨技能表举例这两张表的拆法直接影响后续扩展。4.1 核心表拆分订单、服务项目、阿姨技能订单表的核心字段包括订单编号、用户ID、阿姨ID、服务项目ID、预约开始时间、预计结束时间、地址、金额、支付状态、订单状态、备注、创建时间。这里要注意订单编号不要用自增ID直接展示给用户因为那样会把经营数据泄露出去。建议用时间戳随机数生成一个20位左右的业务单号。服务项目表不要和订单表混在一起。虽然家政系统服务项目数量不多但类型会变比如“日常保洁”“玻璃清洗”“家电清洗”各有不同的单价和服务时长。单独建表之后后续改价格、加项目都很方便。类似地阿姨技能表和阿姨表也要分开因为一个阿姨会多项服务一个服务项对应多个阿姨是多对多关系中间加一张关系表最合适。这里给出一个简化的表结构设计思路表名关键字段说明sys_userid, username, password, role, phone统一用户表用角色区分用户、管理员、阿姨service_itemid, item_name, price, duration服务项目与单价、时长service_workerid, user_id, real_name, score, status阿姨档案关联用户表worker_skillid, worker_id, item_id阿姨与技能的多对多关系service_orderid, order_no, user_id, worker_id, item_id, address, start_time, end_time, amount, order_status核心订单表4.2 时间、金额、状态三类字段的坑时间字段是我第一个要提醒的。项目里所有时间字段我都统一用datetime并且明确分配start_time和end_time两个字段而不是只存一个appoint_time。只用单个时间字段的唯一后果就是你永远没法判断这个订单到底占用了阿姨多久也就没法做时间冲突校验。另外更新时间建议用update_time配合自动填充在MyBatis-Plus里用TableField(fill FieldFill.INSERT_UPDATE)就能实现这块配置很省事不要漏。金额字段是大坑。金额永远不要用float或double精度会出问题。我在第一版系统里就吃过亏一条99.9元的订单算上优惠和支付接口回调最后数据库里存成了99.8999999界面直接显示异常。后来全部改成decimal(10,2)以分为单位也行但前端展示时会麻烦一些。我的建议是数据库存decimalJava侧用BigDecimal前端展示时保留两位小数一步到位。状态字段不要用字符串存中文比如“待支付”“已完成”一旦要做统计、加状态你就要一个个去比对中文。正确的方式是存数字编码中文描述写在枚举类里前端通过字典表或者接口去转换。这样哪怕以后状态数量从8个变成15个也只是加一个枚举值数据库不用动。4.3 初始化脚本与接口文档的沉淀方式这套项目的交付物里包含了数据库脚本这一点我特别想多说两句。我见过很多项目的数据库脚本是开发过程中随手导出的里面一堆测试数据外键也乱七八糟。我自己的习惯是建一个sql目录按版本放三个文件v1_schema.sql、v1_init_data.sql、v2_upgrade.sql。前两个是全新安装时跑的第三个是升级用的这样客户新环境直接跑前两个就能初始化老环境跑第三个就行。接口文档我用的思路其实很简单每个接口的注释写清楚入参、出参、异常码核心接口画一个简单的调用关系说明。刚开始手写文档确实有点烦但不写的话过两周回头看代码就要骂自己。尤其是订单状态变更这种接口不同角色能调用的接口不同不写清楚后面绝对会有人调错。5. 前端权限控制与联调细节多角色系统的落地经验前端部分看起来就是页面和表单但多角色系统有一个隐藏难点权限控制。用户能看到的是自己的订单和偏好设置管理员能看到的是所有订单和阿姨管理阿姨看到的是派给自己的单子和日程。这三个角色的菜单、按钮、页面完全不一样不能只在后端接口做校验前端也要配合起来。5.1 菜单权限和按钮权限怎么做我采用的方案是“动态路由 按钮权限指令”的组合。用户登录后后端返回当前用户的角色标识和权限列表前端根据角色动态生成菜单。// 简化版动态路由示例 const asyncRoutes { admin: [ { path: /order/list, name: 订单管理 }, { path: /worker/list, name: 阿姨管理 } ], worker: [ { path: /order/mine, name: 我的订单 } ], user: [ { path: /order/create, name: 预约下单 }, { path: /order/mine, name: 我的订单 } ] }这个方案比较直观缺点是权限逻辑会散在路由配置里。对当前系统的角色数量来说完全够用。如果以后要做细粒度的按钮权限比如“管理员可以导出订单客服只能查看”就再配合自定义指令v-permission来控制按钮显示。有一个很隐蔽的坑动态路由一定要在用户登录之后“重新生成”不能只做一次。因为刷新页面后前端状态会重置必须重新拉取用户信息和权限列表再注册路由。否则一刷新菜单就没了。我第一版没做这个处理测试时反复刷新页面就崩了后来才知道要放在全局路由守卫里统一处理。5.2 接口封装、路由守卫和登录态前端请求我统一封装在request.js里统一设置baseURL、超时时间、请求头里的token并在响应拦截器里处理状态码。比如token过期时后端返回401前端拦截器就直接跳转到登录页并清空本地存储。// 简化版axios响应拦截器 service.interceptors.response.use( response { const res response.data if (res.code 200) { return res } if (res.code 401) { router.push(/login) return Promise.reject(new Error(未登录或登录已过期)) } ElMessage.error(res.message) return Promise.reject(new Error(res.message)) }, error { return Promise.reject(error) } )路由守卫做两件事第一未登录的人不能访问需要登录的页面第二已登录的人不应该再访问登录页否则会被绕晕。要注意的是路由守卫只能控制页面跳转真正的安全校验必须在后端完成因为前端的守卫在浏览器环境里是可以被绕过的。千万不要把安全寄托在隐藏按钮上。5.3 前后端联调阶段最容易翻车的地方联调阶段我踩过不少坑列几个最常见的。第一个是日期格式不一致后端返回2024-12-01T10:00:00的LocalDateTime格式前端直接显示成字符串特别难看。后来统一在Jackson配置里改成yyyy-MM-dd HH:mm:ss前端再格式化一层这个问题才算解决。第二个是跨域问题前后端分两个端口跑直接请求会被浏览器拦截。我在后端写了一个CORS配置类允许指定来源开发环境才放开跨域生产环境用Nginx反向代理不在代码层面处理跨域。还有一个联调时特别容易被忽略的状态码问题。很多后端新手习惯在接口里直接返回200表示成功就算业务失败也返回200只把message改成“失败”。这个习惯很糟糕因为前端无法通过状态码快速判断只能去解析message遇到异常还会被拦截器当成功处理。我的约定是HTTP状态码永远反映请求本身是否成功业务状态码才反映业务逻辑是否成功两者不要混用。6. 部署上线与运营阶段的二次优化系统功能做完不代表项目结束。真正能拉开差距的是部署细节和上线后的数据观察。这里分享一下我用这套Spring Boot Vue系统做部署时的清单以及上线后最值得优化的几个方向。6.1 服务器部署清单与常见配置问题部署方案我用的是最简单也最稳的一档一台云服务器后端打jar包用systemd或者Docker跑前端用Nginx托管静态文件并做反向代理。前后端分离部署的意思是Nginx负责托管Vue构建出来的dist目录同时把/api开头的请求转发给后端的8080端口。配置里最容易出问题的地方有三个。一是Nginx的client_max_body_size默认值只有1m用户上传头像或者服务图片稍微大点就报413要显式调大。二是后端Spring Boot里要配置multipart大小限制两者得匹配。三是Redis和数据库都要做本地部署如果预算有限不能用云数据库至少要注意密码不能明文写死在配置文件里我习惯用环境变量覆盖默认值。6.2 上线后的数据统计需求系统跑起来之后客户的第一个追问永远是“数据怎么样”。我提前在系统里加了一个简单的统计面板今日新增订单、待派单数量、已完成订单、各服务项目占比、阿姨评分排行。这些指标的SQL都不复杂Group By一下就能出来但千万别等客户提出需求再临时写页面会很被动。统计里面最值得关注的是“派单平均耗时”也就是从用户支付完成到管理员派单成功的时间间隔。这个指标直接反映运营效率如果平均值超过30分钟说明后台处理速度跟不上要优先优化管理员端的操作流畅度而不是加更多花哨功能。6.3 后续扩展方向家政服务系统的天花板其实很高做完基础单子流转后后面还有几个值得扩展的方向。比如会员卡和套餐很多用户是包月保洁一个套餐对应多次服务次数这个复杂度在权益核销上。再比如阿姨的定位签到用户最关心“阿姨到了没”后端接一个地图接口就能实现。还有一块很容易被忽略的是“服务流程的标准化记录”。用户家里的宠物要提前沟通进门要穿鞋套结束要拍照确认这些规则可以在系统里做成“服务须知”连着验收环节一起推送给用户签收减少售后扯皮。老实说这套系统做完之后最让我受益的不是代码本身而是对整个“服务履约”业务的理解。如果你从零开始做类似项目我最大的建议是先把订单状态列全把时间冲突想清楚把数据库的金额字段定成decimal剩下的事情都不复杂。这三个地方稳了项目就成功了大半。最后分享一个我常用的土办法每开发完一个接口就模拟一个完整的用户下单流程从登录到下单到派单到完成全链路跑一遍。这个流程只要不报错说明核心链路就通了一大半。剩下的界面细节和边角逻辑慢慢磨就行。