这两年做同城服务类项目的朋友越来越多,尤其是宠物上门喂食、遛狗这类需求,疫情后增长势头一直很猛。我手头刚好整理了一套完整可跑的前后端分离实现,技术栈就是SpringBoot+Vue+MyBatis+MySQL,源码和部署流程都齐全。这篇文章我会直接把这套同城上门喂遛宠物系统的架构思路、核心功能拆分、数据库设计、关键代码实现到本地部署的完整链路讲清楚。不管是打算接私活、做毕业设计,还是想自己创业跑通一个最小可行产品,这套东西都能给你省下大量从零折腾的时间。
先说清楚这套系统到底解决什么问题。宠物主人出差、加班、应酬的时候,家里的猫狗没人管,需要一个可信赖的人上门喂食、换水、铲屎、遛弯。系统要打通的是“主人下单—平台派单/抢单—服务人员接单—上门服务—确认完成—评价付款”这条完整闭环。技术上选前后端分离,是因为这套业务天然分为“管理端+用户端+服务端”多角色场景,Vue做页面交互,SpringBoot提供接口,MyBatis负责SQL层,MySQL存业务数据,工程结构清晰、对新手友好、也方便后期扩展小程序或者App。
全文我尽量按实际动手顺序来讲,从设计思路到表结构,再到接口实现和前端页面,最后是部署排坑。内容偏长,建议收藏了慢慢看。
1. 先搞清楚业务边界与系统角色划分
1.1 上门喂遛宠物这事,系统里到底要管哪些关键环节
很多人一上来就写代码,结果做着做着发现业务边界一团浆糊。我这里先帮大家把核心业务链路梳理清楚。一套同城上门喂遛宠物系统,最核心的用户流程其实是这几条:
- 宠物主人在微信或网页端注册登录,创建自己的宠物档案(品种、年龄、饮食习惯、是否怕生、疫苗情况等);
- 主人发起服务订单,选择服务类型(上门喂食、遛狗、宠物陪玩、铲屎清洁等),选择服务时间和地址;
- 系统根据地址和服务时间匹配可接单的服务人员(也就是遛狗师/喂养师),可以做成抢单模式,也可以做成指派模式;
- 服务人员接单后,按约定时间上门服务,服务过程中可能需要拍照上传、打卡,主人可以实时看到订单状态;
- 服务完成后,主人确认验收,进行支付和评价,服务人员获得相应收入。
这就涉及三类角色的权限区分:普通用户(宠物主人)、服务人员(接单方)、平台管理员(审核、管理、统计)。如果只做一个“表加增删改查”的简单版本,看起来功能能跑,但一上线就会被各种状态流转和异常情况搞崩。比如订单已经派出去、服务人员临时有事取消怎么办?主人下单后未支付订单超时怎么办?服务人员接单和手机端刷新重复提交怎么办?这些都是需要提前在架构层面想清楚的。
这套系统的做法是把订单状态做成状态机,再用前端按钮权限和后端接口鉴权双保险,避免用户越权操作。数据模型上把用户、宠物、订单、服务项目、评价、支付流水拆成独立模块,互相之间只通过ID关联,做到业务数据可追溯。
1.2 技术选型为什么是SpringBoot+Vue+MyBatis+MySQL,而不是其他组合
这可能是很多同学纠结的第一个问题。有些朋友一上来就推荐微服务、Redis、RabbitMQ,但实际上同城喂遛宠物这种业务规模,在早期阶段用微服务纯属给自己找麻烦。选择SpringBoot+Vue+MyBatis+MySQL这套组合,核心逻辑是“投入产出比最高”。
SpringBoot是目前Java后端开发事实上的标准框架,内置Tomcat,起步依赖管理方便,社区资料多,遇到问题搜索基本都能解决。Vue作为前端框架,学习曲线平缓,响应式数据绑定对订单状态这种频繁变化的场景非常友好。MyBatis做持久层比JPA更容易控制SQL,尤其适合订单报表、多表联查这类复杂查询,半自动化的特性也更容易排查线上SQL问题。MySQL则完全够用,单机部署、事务支持完善、运维简单。
还有一个很重要的原因是这套技术栈人才储备量大。找人接手、找开源代码、招实习生都容易,项目不会因为某个人离职就死掉。之前我见过一个项目用了冷门ORM框架和自定义前端脚手架,结果维护成本高得离谱,踩坑都没地方问。做项目选型,流行度和团队熟悉度本身就是重要指标。
2. 数据库设计:一张订单表撑起整个业务闭环
2.1 核心表结构拆解:用户表、宠物表、服务人员表
数据库是一套业务系统的地基。地基没打好,后面写多少代码都白搭。我按模块给大家拆一下这套系统的核心表结构,实际源码里就是按这套模板初始化的。
用户表(sys_user)是登录入口,字段设计上要区分三类角色:
- id主键自增,用户名、密码(BCrypt加密存储)、手机号、头像地址;
- role字段做角色区分:0代表普通用户,1代表服务人员,2代表管理员;
- 状态字段:1正常,0封禁;
- 创建时间和更新时间,所有表都保留这两个字段,方便排查问题。
宠物表(pet_info)是宠物主人侧的档案信息,在这里要下点功夫,因为这是上门服务安全性的第一道保障:
- id、用户ID(外键关联用户表)、宠物名称、品种、年龄、体重;
- 是否接种疫苗、是否有攻击性、是否绝育,这些字段在上门前必须展示给服务人员;
- 饮食习惯备注、用药备注(比如每天几点喂药)、紧急联系人电话;
- 宠物照片URL,用于主人展示和订单详情展示。
服务人员表(service_worker)单独拆出来的原因,是服务人员除了基础用户信息之外,还需要审核资质和服务评分:
- id、用户ID(关联用户表)、真实姓名、身份证号(脱敏存储)、服务区域(经纬度或区域码);
- 服务类型资质(喂猫、遛狗、宠物护理)、个人介绍、服务价格;
- 评分均值,由订单完成后用户评价算出来;
- 审核状态:0待审核,1审核通过,2驳回。平台必须审核服务人员资质,这是底线。
这三张表是基础。订单表、评价表、支付流水表在它们之上做业务流转。我特别强调一下:身份证号这类敏感信息不要在数据库里明文存,至少要做加密处理或者脱敏展示,这是正规项目的基本素养。
2.2 订单表与服务记录表:状态机落地的关键
订单表(order_info)是整个系统的核心。字段设计不能只想着“保存一次数据”,还要想着“支撑整个订单生命周期”。核心字段至少覆盖:
- id、订单编号(业务上用来给用户看的,不要直接用自增id);
- 用户ID、服务人员ID、宠物ID;
- 服务类型:1上门喂食,2遛狗,3宠物陪玩,4综合服务;
- 服务开始时间、结束时间、服务地址(省市区+详细地址+经纬度);
- 订单金额、优惠金额、实付金额、支付方式;
- 订单状态,这里重点讲一下状态定义:0待支付、1待接单、2已接单、3服务中、4待确认、5已完成、6已取消、7退款中、8已退款;
- 备注、紧急联系电话、创建时间、更新时间。
这个状态定义我是踩过坑的。第一次做类似项目时,我把状态设计成“0未完成、1已完成”两个状态,结果后面业务一扩展直接推倒重来了。订单状态字段千万不能省,宁可多设计几个中间状态,也不要用布尔值去代替。比如“服务中”和“待确认”如果合并成一个状态,用户端和服务端看到的操作按钮就会乱掉。
服务记录表(service_record)记录每次上门服务的动作明细,算是订单表的附件:
- id、订单ID、服务人员ID;
- 上门时间、离开时间、服务内容(遛狗时长、喂食照片、宠物状态描述);
- 服务现场照片1、照片2、照片3,用URL存储;
- 是否按时到达、是否异常反馈、用户确认时间。
这套设计的好处是,平台如果想做“服务质量回溯”,从订单表和服务记录表就能拼出完整的证据链。以后用户投诉、保险理赔、服务人员绩效评估都有数据支撑。
2.3 MyBatis映射文件怎么组织更清晰
MyBatis在项目里的组织方式,我建议按模块分文件,不要所有SQL堆在一个XML里。这套系统的做法是:
- mapper接口定义一个模块一个,比如OrderMapper、UserMapper、PetMapper、EvaluateMapper;
- XML文件按照对应的Mapper接口一一对应,放在resources/mapper目录下;
- 简单的单表增删改查用注解直接写在接口上,复杂动态查询写XML。
举个订单查询的例子,后端接口要支持用户按状态筛选自己的订单列表,SQL要能动态拼条件。如果全写在XML里,就是:
<select id="selectOrderList" resultType="com.example.pet.entity.OrderInfo"> SELECT * FROM order_info <where> <if test="userId != null and userId != ''"> AND user_id = #{userId} </if> <if test="status != null"> AND status = #{status} </if> <if test="startTime != null"> AND create_time >= #{startTime} </if> </where> ORDER BY create_time DESC LIMIT #{offset}, #{pageSize} </select>注意排序字段和分页参数一定要用#{}预编译,不要用${}直接拼接,这能防SQL注入,也能提高执行效率。MyBatis还有一个很实用的配置是驼峰命名自动映射,数据库字段的snake_case可以自动映射到Java属性的驼峰命名,减少了一堆resultMap手写工作量。
3. 后端核心功能实现:从登录鉴权到订单流转
3.1 登录鉴权与角色权限控制
这套系统的登录用的是JWT(JSON Web Token),实现逻辑不复杂但很实用。用户登录成功后,后端生成一个加密token返回给前端,前端存到localStorage或pinia/vuex里,每次请求在请求头带上Authorization字段,后端用拦截器校验token有效性。
JWT有个特点是无状态的,后端不用存session,这在前后端分离架构下特别合适。但要注意token过期时间的设置,我一般设置为2小时有效,同时要求前端在token即将过期前调用刷新接口续期。不然用户正下单填到一半,突然token失效跳登录页,体验非常糟糕。
权限控制这里要重点说。前端做路由守卫,根据用户角色生成不同的菜单;后端做接口权限校验,拦截器里解析出当前用户角色,判断是否能访问某个接口。两层都做,防止有人绕过前端直接调接口。比如“服务人员接单”这个动作,前端只有角色为1(服务人员)的用户才显示接单按钮,后端在接收请求时也要校验当前登录用户角色确实是服务人员,否则返回403。
3.2 订单状态机与并发防重
订单状态机是这个项目里最有含金量的部分。我给大家画一张状态流转的逻辑(不画图,用文字描述):
- 状态0待支付:用户提交订单后进入,支付成功后变1待接单,超时30分钟未支付自动取消(定时任务扫描);
- 状态1待接单:服务人员看到可抢订单,抢单成功后变2已接单;
- 状态2已接单:服务人员到达现场点击“开始服务”,变3服务中;
- 状态3服务中:服务人员完成服务点击“完成服务”,变4待确认;
- 状态4待确认:用户确认服务无误,变5已完成,同时触发支付确认和评价;
- 任何非终态都有机会走6已取消或7退款中。
这个状态机看着不复杂,但实现的时候有个最容易出bug的点:并发防重。想象一下多个服务人员同时抢同一个订单,如果后端代码先查订单状态、再更新订单状态,两个请求同时进来都查到待接单,然后都执行更新,订单就被两个服务人员同时接走了。解决办法是更新SQL里加上状态条件:
int rows = orderMapper.updateStatusAndWorker( orderId, workerId, OrderStatus.WAIT_ACCEPT.getCode(), OrderStatus.ACCEPTED.getCode() ); if (rows == 0) { // 抢单失败,订单状态已被别人修改 throw new BizException("手慢了,订单已被抢走"); }这里利用数据库行锁和更新行数来判断是否抢单成功,简单高效。我再补充一个细节:数据库连接池配置上,更新操作的事务隔离级别使用默认的READ_COMMITTED就够了,不需要调成SERIALIZABLE,否则并发性能会明显下降。
定时任务处理超时订单,推荐用Spring自带的@Scheduled注解,加一个开关配置,每天凌晨扫描一次待支付超过30分钟的订单自动取消,同时释放对应的服务时间段,避免服务人员那边被无效订单占住排期。
3.3 文件上传与图片存储
上门喂遛宠物这种业务,现场照片是重要凭证。服务人员要能拍照上传,这里就要处理图片上传功能。我用的是本地存储方式,配置一个上传目录,SpringBoot接收MultipartFile后保存到磁盘,同时把相对路径返回给前端,前端拼上访问前缀展示图片。
不推荐把图片存到MySQL的Blob字段里,虽然也能跑,但数据库会迅速膨胀、备份困难。正确做法是数据库只存路径,图片文件放磁盘或云对象存储。如果线上部署,可以把上传目录配置成云存储的挂载目录,或者换成对象存储SDK,工程改动都相对可控。另外图片一定要做大小限制和格式校验,我之前见过有人往服务器传了10MB的gif导致页面卡死,这种低级问题在代码里限制一下就好。
4. Vue前端实现要点与页面链路
4.1 前端项目结构与路由权限控制
前端这边我用的是Vue3+Vite+Pinia+Element Plus的组合,整套项目从npm create vue初始化开始。目录结构按视图模块拆分:
- views/user:用户端页面,宠物档案、下单页、订单列表、订单详情、个人中心;
- views/worker:服务端页面,可抢订单列表、我的接单、服务记录、收入统计;
- views/admin:管理后台页面,用户管理、服务人员审核、订单管理、数据统计;
- api目录:每个模块一个请求文件,统一封装axios实例;
- router目录:定义路由和路由守卫。
路由守卫的逻辑我写在全局前置守卫里,每次跳转前检查本地是否有token,没有就跳登录页;有token则根据当前用户角色过滤可访问路由,遇到无权访问的路由跳转到403提示页。这里要注意的是,Vue的router.beforeEach里异步获取用户信息的操作会阻塞路由跳转,我建议在应用启动时先调用一次获取当前用户信息接口,把用户信息存到pinia里,后续路由守卫直接用内存数据判断,避免每次跳转都发请求。
4.2 axios请求封装与后端联调细节
axios请求封装这块,我用拦截器统一做了几件事:请求前带上token,响应后统一处理业务状态码,捕获HTTP异常弹出提示。响应体的设计是统一格式:
{ "code": 200, "message": "success", "data": {} }前端拿到code=200才认为业务成功,其余的code统一弹message提示。这样后端业务异常和HTTP异常分离,前端处理起来逻辑清晰。HTTP状态码我建议只保留200、401、403、404、500这几种,业务上的“抢单失败”“订单状态不对”等错误一律放在业务code里返回,这样前端只需要在后端响应拦截器里统一处理401跳登录,其他的按code的message提示用户就行。
这里有个小坑要提醒:axios默认的response.data是后端返回的body,如果你不小心在后端返回了字符串而非JSON对象,前端拿到的data直接是字符串,后续data.code会报undefined。排查方法是在后端全局异常处理器里统一包装返回值,确保任何情况下前端收到的都是标准JSON结构。
4.3 地图选点与服务地址管理
同城服务离不开地址。前端的实现方案是接入高德地图或百度地图的JavaScript API,在用户下单页面嵌入一个地图组件,让用户点击地图选点后自动回填经纬度和详细地址。经纬度存到数据库后,后续可以做服务人员距离排序、片区划分这些进阶功能。
地图组件这块,我用的是vue-amap或@amap/amap-jsapi-loader封装,关键代码并不复杂。核心是监听地图点击事件设置标记点,再调用逆地理编码接口把经纬度转成文字地址。地址保存在两个地方:一个存到订单表方便下单时快照,一个存到用户的常用地址簿里方便下次直接选择。如果小程序端做不了地图选点,也可以退而求其次使用微信的wx.chooseLocation接口,返回的经纬度格式也是兼容的。
5. 本地部署与启动排坑实录
5.1 环境准备:JDK、Node、MySQL版本选择
说完代码说部署。这套系统的本地开发环境,我的推荐版本是:
- JDK 1.8或11,SpringBoot 2.7.x系列。不要贸然用SpringBoot 3.x,因为SpringBoot 3是基于JDK 17的,一些老版本的MyBatis Starter兼容性会出问题;
- Node.js 16或18,Vite要求Node版本不能太低,我建议装18以上;
- MySQL 5.7或8.0都可以,但要注意数据库连接驱动的版本差异。MySQL 8需要引入mysql-connector-java 8.x,并且连接URL要指定useSSL=false和serverTimezone=Asia/Shanghai,否则连数据库时会报时区异常;
- Maven 3.6+,用IDEA开发的话内置Maven基本够用。
IDE方面,后端用IDEA,前端用VSCode或WebStorm,数据库可视化工具用Navicat或DBeaver。如果电脑配置一般,IDEA建议开省电模式,不然打开多个大文件会很卡。
5.2 后端启动步骤与常见报错排查
后端启动步骤,我按实际操盘顺序列一遍:
- 用IDEA打开后端源码目录,等Maven自动下载依赖,这一步要保证网络稳定,如果下载慢可以换成阿里云的Maven镜像;
- 修改application.yml配置文件里的数据库连接信息,用户名、密码、库名改成自己本地的;
- 先执行源码里自带的db.sql脚本,在MySQL里建库建表并插入初始数据;
- 找到主启动类,右键Run,看到Spring Boot的启动日志出现“Started”字样,说明启动成功;
- 用浏览器或Postman访问http://localhost:8080/api/ping,返回success就说明后端没挂。
我在这套系统上遇到过两个高频报错,提前给大家打完预防针。第一个是数据库连接失败,报Communications link failure,排查思路是:MySQL是否启动→端口是否3306→账号密码是否正确→库是否存在→防火墙是否拦截。第二个是启动时端口被占用,报Port 8080 was already in use,解决方式是换端口或在命令行杀掉占用进程,Windows用netstat -ano | findstr 8080查到PID后taskkill /F /PID。
5.3 前端启动步骤与跨域处理
前端启动要简单很多。进入项目目录后:
- 执行npm install安装依赖,如果安装过程中报错,优先检查Node版本是否满足package.json里的engines要求;
- 修改前端项目的请求环境配置文件,一般是.env.development,把VITE_API_BASE_URL改成http://localhost:8080/api;
- 执行npm run dev启动开发服务器,默认端口5173;
- 浏览器访问http://localhost:5173,看到登录页就说明前端没问题。
前后端分离模式下,最容易遇到的就是跨域问题。前端访问后端接口,端口不同会被浏览器的同源策略拦截。我在后端做了一个全局CORS配置类,允许http://localhost:5173的跨域请求,并且允许携带token请求头。这里补充一点:如果你后端配置了拦截器校验token,那么预检请求OPTIONS是不能被拦截的,否则前端会被跨域坑到怀疑人生。正确做法是CORS配置里直接放行OPTIONS请求。
5.4 数据库初始化脚本与演示数据
源码里的数据库脚本是分两部分组织的:建表和初始化数据。建表部分包含上面提到的所有核心表,初始化数据部分预置了一个管理员账号(admin/admin123)、两个测试宠物主人账号、一个审核通过的服务人员账号,以及几条不同状态的模拟订单。
演示数据特别重要。没有数据的空系统,前端页面打开全是空白,根本没法演示功能。我建议初始化数据至少覆盖:不同状态的订单(待支付1条、待接单1条、服务中1条、已完成2条),这样前端每个状态的分页列表都能看到效果。另外把服务人员的评分初始化为4.8、接单量初始化为300条,首页服务人员列表看起来才不寒酸。
6. 我把这套源码再扩展开:线上发布要考虑的进阶点
本地跑通只算完成了一半。如果真要上线运营,有几件事是最容易被忽略的。
第一个是数据库备份。定时用mysqldump把库导出到备份目录,至少每天一次。我见过太多人直到数据库误删才开始后悔,那时候说什么都晚了。第二个是服务器部署方案,最简单的是买一台云服务器,装宝塔面板,后端用Maven打包成jar包后通过脚本启动,前端npm run build生成dist目录交给Nginx托管,再把Nginx里配置一下反向代理,把/api前缀转发到后端的8080端口。这套部署流程我大概踩过两三次坑才理顺,最核心的点就是Nginx的location /api这一段不能配错,否则前端请求全部404。
第三个是安全加固。修改默认的MySQL密码、SpringBoot的Actuator端点不要暴露公网、服务人员上传的图片目录要禁止执行脚本类文件。如果是在云平台部署,建议再加上简单防火墙配置,只放行80、443和22端口。
第四个是支付对接。这套源码里我留了支付接口的模拟实现,方便本地测试时跳过真实支付。真要对接微信支付或支付宝,还需要申请商户号、配置证书、回调验签等一堆工作。支付这块业务逻辑复杂,建议单独拿一个迭代周期来做,不要和核心功能混在一起上线。
7. 常见问题速查表:照着修,省一半时间
我把这段时间被问到最多的问题整理成一个速查表,大家按需查看就行。
| 现象 | 原因 | 解决方法 |
|---|---|---|
| npm install卡住不动 | 默认镜像源慢 | 设置npmmirror源:npm config set registry https://registry.npmmirror.com |
| 前端请求后端跨域 | Nginx或CORS未配置 | 后端加全局CORS配置,Nginx用proxy_pass代理/api路径 |
| 图片上传失败 | 上传目录不存在或权限不足 | 代码中自动创建目录,或在配置文件中指定一个已有的可写目录 |
| 登录后接口返回401 | token过期或未携带 | 检查前端axios拦截器是否在请求头带上Authorization |
| 抢单接口并发异常 | 缺少防重更新条件 | 更新SQL加上status条件并使用返回值判断 |
| 数据库连接失败 | 时区或SSL问题 | 连接URL加useSSL=false&serverTimezone=Asia/Shanghai |
| 中文乱码 | 控制台和MySQL字符集不一致 | 数据库连接增加characterEncoding=utf8,前端页面设置UTF-8,Linux系统检查LANG |
| 部署后Nginx 404 | 前端文件路径不对 | 确认dist目录上传位置,和Nginx的root配置保持一致 |
这套项目的排错核心思路,我总结一句话:从前端请求发起开始,一步步往后端链路排查,先看浏览器Network面板请求是否发出,再看后端日志报错,最后定位到SQL和数据库层。不要一上来就怀疑代码,大多数问题出在环境和配置层。
8. 最后说点实际的:这套系统的二次开发方向
如果你拿到这套源码,不知道下一步该往哪里改,我根据实际运营经验给你几个方向参考。第一个方向是做多城市分站模式:当前系统是按同城的单城市设计,可以扩展一个城市字段,把服务人员、订单、价格都按城市隔离,这样就能复制到多个城市运营。第二个方向是加上宠物寄养或者宠物日托服务:在服务类型里增加一个“寄养”选项,订单流程从上门服务变成预约送养、到店寄养、接回宠物,数据库层面只需要增加一个寄养门店表和寄养状态字段。第三个方向是增加营销玩法:新客立减券、邀请有礼、会员卡包月服务,这些都是在支付模块后面加一个优惠券系统就能实现的。
我个人在实际操作中的体会是,这类同城服务项目的成败,三分靠技术,七分靠运营规则。技术上只要保证订单状态不会乱、支付金额不会错、评价数据能沉淀下来,就足够撑起早期业务了。与其前期花时间在微服务、容器化这些“听起来很高级”的东西上,不如把核心业务链路跑通,早点让用户用上。这套前后端分离的同城上门喂遛宠物系统,正是按这个思路做的最小完整闭环,希望对你搭建自己的项目能有点实实在在的帮助。