资讯动态

SpringBoot+Vue+MyBatis+MySQL农家乐系统全栈实战

发布时间:2026/10/17 15:03:29 来源:尧图企业网站定制
做农家乐管理平台这套系统前后折腾了将近三个月。中途推翻过一版数据库设计踩过跨域、事务失效、前端路由守卫失效这些老坑最终赶在节前把整个项目跑通上线。这套源码基于SpringBoot Vue MyBatis MySQL面向农家乐的实际经营场景做了完整的业务闭环从预约、入住、点餐到结算、评价、数据分析都有覆盖。如果你正在做类似的管理系统、又不想从零开始搭骨架或者想找一个能写进简历里的全栈实战项目做参考这篇文章值得花几分钟看完。我会把项目的整体设计思路、核心数据表结构、后端关键实现、前端页面拆分、部署要点以及实际开发中踩过的那些坑全部摊开来聊一遍。1. 项目整体设计与技术选型思路1.1 这个平台到底要解决什么问题传统农家乐的经营痛点做过的人都知道房态靠脑子记、订单写在小本子上、菜品价格改了墙上贴张纸、月底盘点营收全靠算盘珠子。客人打电话来订房老板翻半天本子也不知道到底还有没有空房间。这些问题在小体量的时候忍忍就过去了但是当客房超过十间、还要承接团建和亲子游活动的时候纯靠人工管理就会出现两个后果超卖导致客诉账目混乱导致利润缩水。这套系统要做的就是把农家乐里的核心经营场景全部数字化。用户端能浏览农家乐信息、查看房态日历、在线预约客房、提前点菜、下单活动项目管理端能维护房态、审核预约单、处理点餐订单、管理菜品库存、查看营收统计。整个业务流程环环相扣不是那种简单的增删改查CRUD练习而是真正能贴近实际经营状态的管理闭环。我把系统拆成了两个端面向游客的C端小程序风格页面和面向老板/员工的管理后台。项目采用前后端分离的结构后端提供纯API接口前端根据角色不同渲染不同页面。这样做一方面是为了开发时职责清晰另一方面也方便后续扩展比如以后想让客人直接用手机扫码点餐就可以在不改动后端核心逻辑的前提下单独再做一个H5页面。1.2 技术栈选型为什么是SpringBoot Vue MyBatis MySQL这套技术组合放在2025年看依然是非常稳健的性价比之选。SpringBoot 2.7以上的版本已经非常成熟自动配置、内置Tomcat、生态丰富开发效率比SSH那套老古董高出几个量级。Vue不管是2还是3配上Element UI或者Element Plus做管理后台的速度都很快。MyBatis在复杂报表SQL和动态查询这块非常灵活比JPA更容易控制SQL的执行细节。MySQL作为开源数据库农家乐这种中小体量项目跑起来毫无压力。我选型时特意坚持了这几个原则第一不追求新框架选团队经验最熟、资料最多的组合遇到问题搜得到答案比框架炫技重要得多第二不用太重的基础设施不需要引入消息队列、缓存中间件这些避免给一个小体量项目增加不必要的运维负担第三MyBatis逻辑清晰每一条SQL都明明白白写在XML里后期接手的同学容易看懂。很多人纠结用MyBatis还是MyBatis-Plus我的建议是单表简单操作可以用Plus减少代码量但是多表关联和复杂统计还是写XML最稳。1.3 功能模块划分与系统角色设计系统角色分为三类游客/注册用户、农家乐管理员、系统超级管理员。游客可以浏览平台上的所有农家乐信息注册登录后可以提交预约、点餐、取消订单、写评价。农家乐管理员负责本店的核心运营操作包括设置房态价格、接单确认、录入菜品、核销预约。超级管理员则管理整个平台的农家乐入驻审核和用户状态。从功能模块来看主要分为七大块农家乐信息管理农家乐简介、图片轮播、设施列表、位置地图客房管理房间类型、房价日历、房态开关、房间照片预约订单管理订单提交、审核、改期、取消、入住核销、退房餐饮点餐管理菜品分类、菜品上架下架、购物车、订单结算活动项目管理采摘、钓鱼、烧烤等活动的场次和价格配置评价与反馈管理游客评价、管理员回复、评分统计数据统计看板预约趋势、营收统计、热门菜品排行、房态利用率每个模块在数据库里都有对应的核心表支撑模块之间的业务关系靠外键逻辑关联和状态机流转串联起来。2. 数据模型设计一张表关系图背后的业务思考2.1 核心数据表结构与字段设计整个数据库我一共设计了十几张表这里把最核心的几张列出来。第一张是用户表包含主键id、用户名、密码加密存储、手机号、用户角色、头像、注册时间、状态。密码必须加密项目里我用的是BCrypt不用MD5这种已经过时的弱加密。第二张是农家乐信息表包含主键id、商户名称、简介、详细地址、联系电话、主图、营业状态、经纬度、创建者id。第三张是客房表包含房间id、所属农家乐id、房型名称大床房/标准间/家庭房、可住人数、房间数量、基础价格、当前状态。预约订单表是业务核心字段设计上花了我很多心思。订单号用了业务统一编码规则比如 YY 开头加日期加四位序号。关键字段包括预约单号、用户id、农家乐id、客房类型id、入住日期、离店日期、入住人数、总金额、订单状态、创建时间、支付时间、核销时间、备注。订单状态我专门用int存储配合一个状态枚举类统一管理。菜品表和菜品订单表也类似包含关联订单、单价、数量、小计等字段。这里有一个值得注意的设计细节订单上不仅存了冗余的农家乐名称和房型名称还快照了一份当时的单价。这么做是故意的因为菜品和房价后续一定会调整但是历史订单必须保留下单时刻的价格信息不然月底对账的时候老板会发现订单金额跟当时的挂牌价对不上账根本没法算清。2.2 预约订单状态机的设计与流转订单状态机是这套系统的业务纽带。我最终设计了五个状态待确认、已确认、已入住、已完成、已取消。游客提交预约后进入待确认管理员在后台确认接单后变更为已确认客人到店办理入住变更为已入住退房离店后变更为已完成。待确认状态下游客可以自由取消已确认后取消需要联系管理员操作后台也提供一个取消入口。状态流转不是无脑修改一个字段就完事。每一步状态变更我都记录了操作日志方便日后排查纠纷。比如客人说我明明订过房管理员一查状态日志能清楚地看到订单是何时创建、何时确认、何时取消的。日志表就是简单的主键、订单id、操作前状态、操作后状态、操作人id、操作内容、操作时间七个字段。做一个状态机还有一个好处就是前端页面可以根据状态值来决定按钮显示逻辑。待确认显示等待商家确认已确认显示申请取消已入住显示去评价整个交互链路是自然的后端也屏蔽了大量非法操作的可能。2.3 数据库设计的经验教训我推倒重来的那一次我第一次设计订单表时把入住和离店的日期做成了datetime类型结果做房态冲突检测时SQL写得极其别扭。后来全部改成date类型入住日期就是当天房价按晚数计算逻辑瞬间清爽很多。另一个教训是客房库存问题。我没有设计库存表而是在客房表加了一个房间数量字段做预约校验时通过统计已占用房间数来比对剩余可订数量。用SQL子查询解决虽然性能不是最优但数据正确性完全没问题也避免了一张房态日表带来的海量数据插入。后来在测试过程中我把乱占用的房间数据清理了重新做了一次全量数据清洗才发现外键约束很重要。开发阶段为了图方便表结构里没有建物理外键全靠代码逻辑保证一致性结果某个测试账号因为手动删了农家乐表记录导致关联的菜品数据变成了孤儿数据游客端直接显示了不存在的农家乐。从那以后代码中凡是涉及删除操作都要先做关联检查。3. 后端关键实现SpringBoot MyBatis 的核心细节3.1 工程分层结构与代码组织后端工程我用的是标准的四层结构Controller、Service、Mapper、Entity。Entity对应数据库表Mapper负责数据访问Service写业务逻辑Controller只做参数接收和结果返回。很多人会把业务逻辑写在Controller里初期开发快但项目一大就乱了。我的原则是Controller越薄越好只做路径分发、参数校验、结果封装。项目顶层还有一些通用模块common包统一返回结果类Result、分页结果包装、异常处理类config包跨域配置、拦截器配置、WebMvc配置security包JWT工具类、拦截器、用户上下文util包日期处理工具、订单号生成器包结构的清晰程度直接决定后期维护的效率。这个项目里有角色权限我一开始就做了拦截器方案不是每写一个接口就复制粘贴权限判断而是在WebMvc配置器里统一注册拦截路径和排除路径一劳永逸。3.2 JWT登录鉴权与拦截器配置实操登录鉴权我用了JWT方案无状态、不需要在服务端保存Session。登录成功后端返回一个token前端存到本地存储之后每个请求带上Authorization请求头拦截器解析token拿到用户id和角色。JWT的核心代码就几步首先是生成token的工具类用HmacSHA256算法签名字符串里面包含用户id、用户名、角色、过期时间。密钥配置在application.yml里注意生产环境不能把密钥写在代码里而应该通过环境变量注入。拦截器里面我继承HandlerInterceptorAdapter重写preHandle方法首先从请求头获取token为空直接返回401。然后调用工具类校验token的签名和有效期解析失败说明token被篡改或过期。解析成功把用户信息放进ThreadLocal后面Service层直接可以从上下文取不用每个方法都传userId参数。放行前再判断接口地址的前缀admin开头的接口必须验证角色为管理员。这里有一个特别值得说的坑跨域配置和拦截器配置的生效顺序。如果跨域配置写得不正确前端浏览器发起OPTIONS预检请求时直接被拦截器拦截并返回401导致真实的POST请求根本发不出去。解决办法是在拦截器的preHandle里先判断请求方法如果是OPTIONS请求直接放行然后跨域配置里用allowedOriginPatterns而不是allowedOriginsSpringBoot 2.4以上版本针对allowCredentials(true)场景下的跨域处理有变化不加allowedOriginPatterns会一直报跨域错误。3.3 MyBatis Mapper编写的关键SQL技巧MyBatis的XML文件是这整套系统的SQL核心。动态SQL标签用得最多的是where if foreach。以客房列表查询为例游客端筛选条件有入住日期、离店日期、入住人数这三个条件可能只传一部分我就用动态SQL拼接。关键代码大概是select idselectAvailableRooms resultTypemap SELECT r.*, (SELECT COUNT(*) FROM reservation_order o WHERE o.room_type_id r.id AND o.status IN (1,2,3) AND #{checkIn} ![CDATA[]] o.check_out AND o.check_in ![CDATA[]] #{checkOut} ) AS occupied_count FROM room_type r where r.farm_id #{farmId} if testpersonNum ! null and personNum ! 0 AND r.max_people #{personNum} /if /where /select这个查询是预约系统的核心逻辑用了一段判断区间重叠的SQL来算房态冲突区间重叠的判断公式是已订订单的入住日期小于当前查询的离店日期且已订订单的离店日期大于当前查询的入住日期。边界值处理要注意两个订单入住和离店日期恰好首尾相接理论上不冲突用严格小于号排除掉。这一段逻辑用画图的方式最好理解两条时间轴交叉判断。还有一个实用的MyBatis技巧就是批量插入。菜品订单明细表一次订单会插入多条记录我用的foreach标签批量insert。之前用循环单条插入两百条数据插了好几百毫秒改成批量后只需几十毫秒。3.4 事务处理和并发下的超卖问题预约提交接口一定要加事务。一个预约操作涉及三件事插入预约订单记录、修改房态临时占用标记、写入操作日志。这三件事要么全部成功要么全部回滚。我在Service方法上加Transactional注解同时把事务回滚规则指定为抛出Exception即回滚。默认情况下Spring事务只在RuntimeException时回滚而我的业务校验抛出的通常是自定义的BusinessException如果不加rollbackFor Exception.class事务根本不会触发这个细节很容易被忽略我实际测试中发现数据库里多了一条脏数据才排查出来。并发超卖是预约系统的隐藏杀手。两个游客同时提交同一房型的订单后端同时校验剩余房间数可能都通过了校验最终却卖出了超量的房间。解决思路是在判断房态冲突的查询上加悲观锁MySQL里使用for update但要注意for update必须放在事务里执行否则锁会自动释放。不过加锁又带来两个问题第一加锁的SQL必须命中索引否则会锁全表第二高并发场景下性能受影响。项目实际使用中一间农家乐同一时段的订单并发量通常不高悲观锁的代价可以接受数据正确性优先。4. 前端Vue实现从页面拆分到接口联调4.1 项目创建与页面组件划分前端我用的Vue框架配合Element组件库做后台管理界面游客端自己写了一套基于Flex布局的移动端风格页面。项目通过Vue CLI创建路由用Vue Router状态管理用Vuex。目录结构划分为api目录放所有接口请求函数assets放静态资源components放通用组件views按模块分子目录存放页面router集中维护路由表。管理后台的页面组件划分比较清晰按导航菜单一一对应。游客端包含首页、农家乐列表、农家乐详情、预约表单、订单中心、个人中心。这里有一个设计经验详情页拆成了多个子组件农家乐轮播图组件、房间列表组件、菜品展示组件、活动列表组件、评价列表组件父组件负责数据请求子组件只接收props渲染如果后续详情页要增加新的展示区块直接新增子组件就行。4.2 前端路由与权限控制路由是前后端分离项目里比较容易出问题的地方。前端路由表分成三部分公共路由、用户路由、管理端路由。公共路由包含登录注册和游客端页面用户路由是登录后可见的管理端路由通过路由meta上的roles字段标记可访问角色。我在全局前置守卫里做判断用户未登录访问受限页面则重定向到登录页已登录但角色不符合则重定向到首页。实际开发中我踩过一个React风格思维带来的坑就是只做前端路由守卫但后端接口没有做角色校验。有同学直接访问后台接口地址拿走了未公开数据。后来前后端一起处理前端路由守卫管体验后端拦截器管数据安全两者各司其职才形成真正的安全闭环。4.3 Axios封装与统一异常处理前后端交互用的Axios库我封装了一个单例实例设置了baseURL、请求超时时间、请求拦截器和响应拦截器。请求拦截器统一从Vuex里取token设置到Authorization请求头。响应拦截器先判断HTTP状态码非200统一弹出错误提示再判断业务状态码业务码404表示token过期或无效此时清除本地token并跳转登录页。这套响应的包装规范我和后端的约定是所有接口返回统一格式包含code、message、data三个字段。code为200表示成功400表示参数错误401表示未认证403表示权限不足500表示服务器异常。统一格式的好处是前端拦截器可以一套逻辑处理所有异常场景不用每个页面都写错误分支判断。4.4 前后端联调中的小技巧与处理心得联调阶段最频繁的问题是字段命名风格不统一。后端习惯用下划线命名数据库字段直接返回给前端就是snake_case前端习惯用camelCase驼峰命名。我之前在Service层手动做转换写了大量映射代码。后来发现MyBatis配置里直接开启mapUnderscoreToCamelCase选项数据库字段自动映射成驼峰属性。再配合Jackson配置后端实体直接定成驼峰字段前端拿到的JSON就是标准驼峰格式。日期格式化也是一个坑。后端返回LocalDateTime默认序列化成一长串数组前端要自己转换。我在配置里注册了一个Jackson的JavaTimeModule统一设置日期格式为yyyy-MM-dd HH:mm:ss前端拿到的就是可读字符串省掉一堆format方法。联调过程中的跨域问题前面提过还有一个容易踩坑的是本地开发环境Mock数据和后端真实数据切换。我在api目录里统一封装请求函数接口挂了就临时改baseURL到MockServer前端代码逻辑完全不用动排查问题方便很多。5. 系统打包、部署与上线后的常见问题5.1 前后端分离项目部署全流程部署环境是我自己买的一台云服务器系统是CentOS 7配置不高跑这套系统绰绰有余。部署流程分四步安装环境、后端打包、前端打包、配置Nginx。后端打包先用Maven执行clean package命令得到项目jar包。这个阶段要注意测试环境配置和生产环境配置的区分我用了SpringBoot的profile机制application.yml里配置公共项application-prod.yml里放生产环境数据库地址和密钥信息打包命令里指定-Dspring.profiles.activeprod。Jenkins或者简单的Shell脚本都能做自动化发布项目初期我先手动执行等接口稳定后再配置自动化。前端打包执行npm run build生成dist目录里面全是静态文件。把dist目录传到服务器上Nginx配置两块第一块处理静态资源请求location / 指向dist目录配置try_files让Vue Router的history模式路由能正常刷新第二块处理API请求location /api/ 代理到后端接口地址。Nginx配置里还有一个关键点配置proxy_set_header Host和X-Real-IP如果不设置后端获取客户端IP会取到Nginx服务器地址导致日志里全部是127.0.0.1排查问题非常痛苦。5.2 上线后遇到的真实问题与排查方法上线第一周用户反馈访问卡顿。我查了后端日志发现大量慢SQL集中在房态查询接口。原因是这个接口的SQL每次都对订单表做了全表扫描。排查步骤先从慢查询日志入手打开MySQL慢查询日志配置把超过一秒的SQL全部打印出来然后通过EXPLAIN分析执行计划发现关联查询的字段没有索引。给订单表加了复合索引后查询速度从800毫秒降到30毫秒。第二个问题是图片上传后无法访问。开发环境图片存本地磁盘路径直接拼接上服务器后路径对不上。最后定的方案是把图片存的路径在配置里抽出来Nginx单独映射一个 /upload/ 前缀到图片存储目录数据库只存相对路径读取时由前端拼完整地址。这样不管是本地开发还是服务器部署只要配置对得上就都能正常显示。还有一个容易被忽略的问题是时区。MySQL连接串里设置的serverTimezone是Asia/Shanghai后端Jackson序列化也设置了时区但有一次服务器系统时区是UTC导致订单创建时间比实际时间少了八小时。排查了老半天最后在启动参数里增加-Duser.timezoneGMT8彻底解决。5.3 系统后续维护与扩展建议项目上线不是终点迭代维护才是长期工作。这套系统的架构给了我很好的扩展空间。包括支付接口对接是下一步要做的目前预约订单还是线下付款后管理员确认状态未来接微信支付后订单流程可以更顺滑。消息通知那块也是可以引入WebSocket做订单状态实时推送游客提交预约后管理员后台弹窗提醒。数据统计模块目前是简单的列表和图表后续可以考虑引入定时任务做日结报表每天凌晨自动汇总前一天的营收、入住率、菜品销量。这个功能我已经规划好了用SpringBoot的Scheduled注解实现定时方法配合MyBatis写一个复杂的汇总SQL一个月内就能落地。最后再分享一些我做这套系统的体会。写代码时觉得状态机设计、事务边界、并发处理这些概念很抽象真正做完一个完整闭环的项目才理解实际业务对代码设计的影响有多大。你在生产环境遇到的一个超卖问题、一个乱掉的订单状态都是设计阶段少思考一步的结果。建议做同类项目的朋友把50%以上的设计精力放在数据建模和状态流转上这些地方想清楚了后面的编码工作只是体力活。至于那些踩过的坑跨域配置、事务回滚、慢查询优化其实都是每个开发者都会经历的成长阶梯遇到一个解决一个踩过的坑越多后面的路越稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑