1. 项目概述与需求拆解1.1 民宿管理系统到底在解决什么问题做这个项目之前我先聊一个很现实的问题酒店管理系统和民宿管理系统看起来都是管房间、管订单但本质上有很大差异。酒店讲究的是标准化房型统一、价格统一、入住退房时间统一民宿则是碎片化经营房东可能只有三五间房每间房的布局、朝向、价格都不同淡旺季定价策略也完全不同甚至同一间房周六和周四的价格都不一样。如果直接把酒店管理系统那套搬过来用房东会疯掉功能做出来没人用。所以“Java基于Spring BootVue的民宿管理系统”这个项目的核心说白了就是围绕“非标房源”做一套管理工具。它要解决的痛点有三个第一房东需要随时发布房源、维护房态、设置差异化价格第二游客需要一个直观的渠道去浏览房源、锁定日期、快速下单第三订单一旦产生双方都要能跟踪状态——待支付、已确认、入住中、已完成、已取消每一环都要清晰。这类项目在各高校的Java课程设计和毕业设计中出现频率极高原因也很直接它覆盖了Web开发几乎全套基本功。后端要用Spring Boot做REST API、JPA或MyBatis操作MySQL、写业务逻辑处理订单状态流转前端要用Vue搞组件化页面、用Vue Router处理页面跳转、用Axios做接口请求还要处理权限、校验、多表关联查询、前端交互反馈这些工程化问题。一套做下来前后端打通基本功基本就扎实了。1.2 这套系统适合谁来参考我先说说我对目标读者的理解。如果你正在准备Java方向的毕设或课程设计手里已经掌握了Java语法和数据库SQL基础但对Spring Boot的自动配置、MyBatis Plus的CRUD封装、Vue的组件通信这些还比较模糊那么这个项目就是一条完整的参考线。跟着搭出来的不只一个表格页面而是一套可以演示、可以答辩、可以继续扩展的完整系统。当然也有不少在职的初级开发会用这类中小型系统练手。我的建议是别把它当成一个纯玩具项目而是把它当成一个入门级的中台系统来做。什么意思就是在设计数据表时别只想着当前页面要什么字段要多想一步。举例订单表里除了“下单用户ID”还要不要冗余“房源名称”和“房东ID”答案是必须冗余否则你前端订单列表每次都要join三张表才能显示一个名称接口慢不说联调时也容易出错。这些都是真实工作里才会遇到的设计权衡这个项目做好了是能沉淀出不少工程经验的。系统功能拆开来说大体上分用户端和管理端两个大块但数据底层共享一套。用户端主要做四件事浏览房源、搜索筛选、查看详情、提交预订。管理端则要复杂一些房源信息的增删改查、房间类型与价格管理、订单全流程处理、用户管理、统计报表。下面我从设计、实现、部署三个角度把整个项目从头到尾拆一遍。2. 技术选型与整体架构设计2.1 为什么是Spring Boot Vue而不是别的组合我先说后端。Spring Boot能成为Java Web项目的默认选择最大原因是它大幅降低了配置成本。做传统SSH或SSM项目时光配置applicationContext.xml、spring-mvc.xml、数据源、事务管理器就能花掉半天而且配置错了报错信息还看不懂。Spring Boot通过自动配置把这一堆东西收敛了你只需要关注业务代码。对民宿管理系统这种业务逻辑清晰、表结构不算极端的项目来说Spring Boot的“约定大于配置”特性正好发挥优势——创建一个Spring Initializr工程引入Web、MyBatis Plus、MySQL Driver、Lombok这几个核心依赖项目骨架就能跑起来。再说前端。Vue的定位非常清晰渐进式框架。它不像React那样需要大量额外选型对一个管理系统来说Vue配合Vue Router和Vuex或Pinia足以覆盖页面路由、状态共享、组件复用。加上Element UI或Element Plus组件库表格、表单、弹窗、日期选择器全都有现成的开发效率极高。前端做民宿管理系统这种中后台应用Vue几乎是高性价比方案。有人可能会问为什么不用Thymeleaf做服务端渲染那是传统单体应用的做法。但“前后端分离”在现在已经是主流Spring Boot只负责提供JSON接口Vue通过Axios消费接口这种模式有非常实际的好处后端可以独立测试API、前端可以独立调试样式交互部署时前端打包成静态文件能用Nginx托管后端跑在Tomcat上互不干扰。从学习角度讲也能逼着你把接口设计做规范——团队协作中这是最重要的基本功之一。2.2 系统整体架构与模块划分整个系统的数据流大致是Vue页面发起请求 → Axios发送HTTP请求到Spring Boot的Controller层 → Controller接收参数并做基础校验 → 转发给Service层处理业务逻辑 → Service通过Mapper/Repository访问MySQL数据库 → 返回JSON数据 → 前端渲染。模块划分上我倾向分成这几个维度模块主要功能角色用户认证模块注册、登录、JWT签发与校验游客/用户房源管理模块房源发布、房间类型管理、价格策略设置房东/管理员预订订单模块浏览搜索、日历选房、下单支付、状态流转用户/管理员评论管理模块入住评价、评分展示、评价审核用户/管理员统计报表模块订单数量、收入汇总、房源热度管理员这里有一点需要特别说明很多入门项目把“房东”和“管理员”混成同一个角色这是设计上偷懒的表现。实际场景中民宿系统通常有三种角色游客未登录、普通用户消费者、管理员平台运营者或房东。房源的发布与维护、订单的确认与核销应该是管理员权限而游客只能浏览和搜索登录注册后才有权限下单。这个差异要在后端接口的权限控制里体现出来可以通过Spring Security或拦截器实现。2.3 为什么选择MyBatis Plus而不是JPA这是我在做技术选型时最纠结的一点。JPA上手理论上次之注解和规范都很统一但实际写多表联查和复杂SQL时用JPA写JPQL或原生SQL都比较别扭而MyBatis Plus则保留了MyBatis对SQL的灵活控制能力同时内置了通用的CRUD方法比如selectById、selectPage、selectList等等单表操作完全不用写SQL复杂查询就像传统MyBatis那样写XML或注解SQL即可。民宿管理系统的业务查询其实有大量“半复杂”场景比如说“查询某房东名下所有房源中剩余房间数大于0的房源”用MP的LambdaQueryWrapper可以写LambdaQueryWrapperHouse wrapper new LambdaQueryWrapper(); wrapper.eq(House::getLandlordId, landlordId) .gt(House::getLeftRooms, 0) .orderByDesc(House::getCreateTime); ListHouse houses houseMapper.selectList(wrapper);这类代码写起来效率极高而且可读性也不差。遇到复杂统计报表时再手动写SQL两者配合非常顺手。所以最终我的建议是如果你做这个项目不需要太纠结选ORM框架MyBatis Plus MySQL是普适性最强的组合网上资料最多、网上遇到问题最容易查到解决方案。3. 数据库设计与核心业务表结构3.1 表结构设计的关键思路民宿管理系统的数据结构我认为是整个项目中最重要的部分——很多人做毕设代码写得还行一看到数据库表设计就露馅了。表设计直接决定了后续的功能能不能顺利实现、联调会不会卡壳。我先说一下核心表共7张用户表、民宿表、房间表或房型表、订单表、评论表、收藏表、轮播图表。如果你想做得更细还能拆出民宿图片表、价格日历表等。但作为“设计与实现”的项目控制在6到8张表是比较合理的范围既能体现出设计能力又不会因为表太多导致工作量爆炸。用户表t_user核心字段包括id、username、password、phone、email、avatar、role、create_time。这里password存的是BCrypt加密后的密文别用明文存。民宿表t_homeid、home_name、address、cover_image、description、landlord_id关联用户ID、audit_status0待审核、1已上架、2已下架、view_count、create_time。这里需要注意民宿不是一个纯粹的实体它同时包含“内容资料”和“状态”状态字段非常重要——前端只展示审核通过且上架状态的房源。房间表t_roomid、home_id、room_name、room_image、guest_count、bed_count、area、price_per_night、stock库存房间数、status。这个表核心在于price_per_night和stock的设计后面做预订时要直接关联这两个字段。订单表t_orderid、order_no、user_id、home_id、room_id、check_in_date、check_out_date、price_per_night、total_price、status、create_time。订单表里冗余了home_id和room_id及快照价格这是因为民宿订单的历史价格可能随日期的不同而变动下单后房东改价也不应该影响已生成的订单。评论表t_commentid、user_id、home_id、order_id、content、score、create_time、reply_content、reply_time。关联order_id是为了确保只有实际消费过的用户才能评价这是防刷评价的关键手段。收藏表t_favoriteid、user_id、home_id、create_time以user_id home_id做唯一索引防止重复收藏。3.2 订单状态字段的设计与流转订单状态是整个系统里最体现业务逻辑的部分。我建议用整数类型存状态然后在代码中定义状态常量类这样数据库体积小前端展示时做映射即可。订单状态流转要清晰0待支付 —— 用户提交订单后进入保留15或30分钟超时自动关闭1已支付/待确认 —— 用户支付成功等待房东确认2已确认/待入住 —— 房东确认订单房间锁定3已入住 —— 办理入住4已退房 —— 办理退房订单完结5已取消 —— 用户或房东取消这个状态流转不复杂但实现时要注意一点——用户取消订单和房东拒绝订单都改成“已取消”状态但操作日志或备注字段要区分是谁取消的。这在实际业务里是一个明显的细节点答辩时你主动提出来会是加分项。3.3 防超卖房间库存扣减的正确姿势做民宿预订系统时有一个非常经典的技术难点高并发下防止房间“超卖”。假设某房源只剩一间房两个用户同一时间下订单系统绝不能同时让两个人都下单成功。这个问题用代码层面说的核心是不能让请求先查询库存再下单修改库存因为查询和修改之间存在时间差。正确姿势是使用数据库层面的原子操作。MyBatis Plus下推荐用一条带条件的更新语句Update(UPDATE t_room SET stock stock - 1 WHERE id #{roomId} AND stock 0) int deductStock(Param(roomId) Long roomId);执行完这条语句判断返回值1表示扣减成功可以继续生成订单0表示库存不足或房间不存在直接返回“房间已满房”。配合事务注解保证扣库存和生成订单要么同时成功、要么同时失败。这里再说一个优化方案在订单表的设计中也可以给用户ID、房间ID、入住日期做联合唯一索引防止同一个用户在同一日期重复下单。索引字段的组合顺序可以根据查询频率来调整但在初学者项目中这个设计方案已经完全够用。4. 后端核心实现与接口设计4.1 项目工程结构与分层无论项目大小分层清晰是长期可维护的基础。民宿管理系统的后端工程我建议的目录结构是src/main/java/com/xxx/homestay/ ├── controller/ # 控制层只负责接收请求和返回结果 ├── service/ # 业务层处理具体业务逻辑和事务 ├── mapper/ # 数据访问层MyBatis Plus的Mapper接口 ├── entity/ # 数据库实体类 ├── dto/ # 数据传输对象接收前端参数或返回前端结果 ├── vo/ # 视图对象比如前端列表页需要展示的商品信息 ├── common/ # 公共类返回值封装、全局异常、通用常量 ├── config/ # 配置文件如跨域、拦截器注册、MyBatisPlus分页插件 └── util/ # 工具类比如JWT工具、日期工具看到这里很多人会有个疑问entity、DTO、VO到底有什么区别我的理解是entity对应数据库字段不直接暴露给前端DTO接收前端传来的参数防止恶意字段注入VO则是专门为前端页面定制的数据结构。比如订单列表页前端要显示“民宿名称”而不是“民宿ID”那么这个字段就要组装到VO里返回。虽然这样多写几个类但工程上很值。4.2 统一返回结果与全局异常处理前后端分离项目一个必须提前约定好的规范就是接口的返回格式。如果没有统一格式前端每个接口都要单独判断一次数据结构和错误码很痛苦。我给大家展示一下我常用的返回类设计Data public class ResultT { private Integer code; // 200成功其他失败 private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }前端得到的所有响应都长这个样用Axios的响应拦截器统一处理如果code为200就正常进入业务逻辑否则直接弹出message提示。这样前端代码师不用每个接口都写一遍错误处理分支。全局异常处理用Spring Boot的RestControllerAdvice注解配合ExceptionHandler把异常统一转化成Result返回。尤其要注意捕获两类异常业务异常自定义的RuntimeException子类和参数校验异常MethodArgumentNotValidException。这样无论你代码里哪里抛了异常返回给前端的格式始终是规整的。4.3 核心接口设计从0到1实现注册登录用户认证模块是系统的门面也是很多新手最懵的地方。我用JWT的方式做无状态认证过程分三步。第一步注册接口。接收用户名、密码、手机号密码用BCrypt加密再入库同时校验用户名是否重复PostMapping(/register) public ResultVoid register(RequestBody Valid RegisterDTO dto) { // 校验用户名是否已存在 LambdaQueryWrapperUser wrapper new LambdaQueryWrapper(); wrapper.eq(User::getUsername, dto.getUsername()); if (userMapper.selectCount(wrapper) 0) { throw new BusinessException(用户名已存在); } User user new User(); user.setUsername(dto.getUsername()); user.setPassword(new BCryptPasswordEncoder().encode(dto.getPassword())); user.setPhone(dto.getPhone()); user.setRole(1); // 默认普通用户 userMapper.insert(user); return Result.success(null); }第二步登录接口。查询用户比对密码签发JWTString token JwtUtil.createToken(user.getId(), user.getUsername(), user.getRole());第三步写一个拦截器或Spring Security过滤器校验请求头里带过来的token。这里很多教程会引导你用Spring Security但说实话对于民宿管理系统这种角色的项目用一个简单的拦截器 JWT工具类完全够了Spring Security学习成本高代码也多。你可以在答辩时坦白说“因为系统角色少、接口权限逻辑简单我用了拦截器做轻量级权限控制同时预留了接入Spring Security的扩展空间。”这个回答很加分。4.4 房源查询接口与多表关联房源列表是用户端访问最频繁的接口也是接口设计中最需要优化的接口。常见需求包括按城市区域搜索、按入住人数筛选、按价格区间筛选、按关键词搜索名称和描述再加上分页。我用MyBatis Plus的分页插件配合条件构造器实现GetMapping(/list) public ResultPageHomeVO list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword, RequestParam(required false) String city, RequestParam(required false) BigDecimal minPrice, RequestParam(required false) BigDecimal maxPrice) { PageHome page new Page(pageNum, pageSize); LambdaQueryWrapperHome wrapper new LambdaQueryWrapper(); wrapper.eq(Home::getAuditStatus, 1) .and(StringUtils.hasText(keyword), w - w.like(Home::getHomeName, keyword).or().like(Home::getDescription, keyword)) .like(StringUtils.hasText(city), Home::getAddress, city) .orderByDesc(Home::getCreateTime); PageHome homePage homeMapper.selectPage(page, wrapper); // 把Home转成HomeVO填充最低价格、封面、评分等信息 return Result.success(homePage); }这里有几个颗粒度比较隐蔽的细节搜索的关键词如果既可能出现在民宿名称里也可能出现在描述中要用and(... )包起来保证关键词和城市条件之间是并列而不是错乱的分页和排序要尽早做避免查全表。4.5 预订下单接口的完整流程下单接口是整个项目中最能体现业务能力的接口前台做日期选择后台要做日期校验、库存校验、价格计算、库存扣减、订单生成一气呵成。伪代码流程如下Transactional(rollbackFor Exception.class) public OrderVO createOrder(OrderDTO dto) { // 1. 校验参数的日期合法性 if (dto.getCheckInDate().isAfter(dto.getCheckOutDate()) || dto.getCheckInDate().isBefore(LocalDate.now())) { throw new BusinessException(日期不合法); } // 2. 根据房间ID查房间信息校验状态 Room room roomMapper.selectById(dto.getRoomId()); if (room null || room.getStatus() ! 1) { throw new BusinessException(房间不存在或已下架); } // 3. 原子扣减库存防止超卖 int rows roomMapper.deductStock(dto.getRoomId()); if (rows 0) { throw new BusinessException(该房间已被订完请选择其他房型); } // 4. 计算总价按晚数 Long days ChronoUnit.DAYS.between(dto.getCheckInDate(), dto.getCheckOutDate()); BigDecimal totalPrice room.getPricePerNight().multiply(BigDecimal.valueOf(days)); // 5. 创建订单记录 Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(dto.getUserId()); order.setHomeId(room.getHomeId()); order.setRoomId(room.getId()); order.setCheckInDate(dto.getCheckInDate()); order.setCheckOutDate(dto.getCheckOutDate()); order.setTotalPrice(totalPrice); order.setStatus(0); // 待支付 orderMapper.insert(order); // 6. 返回订单信息订单号、金额等 return orderVO; }Transactional保证在步骤3出问题后之前插入的数据能全部回滚。这里是踩坑高发区——新手经常忘记加事务注解或者发生异常时没有主动抛出RuntimeException导致库存扣了但订单没生成事后数据对不上。还要说下generateOrderNo()的常见写法yyyyMMddHHmmss 3位随机数 用户ID。这是为了生成方便但并发量高时可能重复。如果追求严谨可以引入雪花算法或数据库自增序列但在毕设项目中时间戳加随机数加用户ID的方式已经够用。5. 前端核心实现与页面交互细节5.1 Vue项目脚手架与依赖安装前端技术栈建议用Vue 3 Vite Vue Router Pinia Element Plus。Vite比Webpack快得多启动项目几秒钟而且Vue 3生态已经非常成熟现在再学Vue 2其实是往过时方向投入。创建工程我推荐直接用Vite官方脚手架npm create vitelatest homestay-web -- --template vue cd homestay-web npm install npm install vue-router4 pinia element-plus axios这里有个很实际的经验npm install在国内经常卡住或下载慢如果你也碰到这个问题直接改用国内镜像源npm config set registry https://registry.npmmirror.comElement Plus推荐使用完整引入或者按需自动引入。对于管理系统来说如果为了省事直接完整引入也完全没问题import ElementPlus from element-plus import element-plus/dist/index.css别小看这一步的折腾很多新手在第一关就被环境配置卡住。如果你是在VSCode环境里开发再装一个Vue官方插件Volar确保Vue单文件组件的语法高亮和拼写检查正常。5.2 路由设计登录守卫与页面权限民宿管理系统前端路由大概分两类用户端页面和管理端页面。用户端包括首页、房源列表页、房源详情页、登录注册页、个人中心页管理端包括民宿管理、订单管理、评论管理、统计报表等页面。用Vue Router时关键是“登录守卫”逻辑。对于需要登录才能访问的页面例如“预订下单页”和“个人中心页”在路由配置中加入meta: { requiresAuth: true }然后在全局前置守卫里做判断router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } })这算是最基础的路由权限控制。如果还想更深入可以动态生成路由——管理员登录后从后端拉取他的菜单权限用router.addRoute()动态添加路由。这在真实后台项目里是标配功能也是面试常考点。民宿管理系统里角色类型少你用静态路由加守卫已经能说明问题但如果想体现进阶水平自己动手实现一遍动态路由成长会很大。5.3 Axios封装与前端请求统一处理前端所有接口请求都应该通过一个封装好的Axios实例发出而不是在每个组件里各自引入Axios直接请求。统一封装的好处非常多所有请求自动带上token、所有响应统一处理错误提示、接口地址统一配置。核心代码长这样import axios from axios import { ElMessage } from element-plus import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器自动携带token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization token } return config }) // 响应拦截器统一处理业务码 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { ElMessage.error(登录已过期请重新登录) localStorage.removeItem(token) router.push(/login) } else { ElMessage.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default request这里有个很关键的细节baseURL: /api不是走绝对的接口地址而是走相对地址。开发环境下通过Vite的devServer配置代理将/api转发到Spring Boot的localhost:8080生产环境下用Nginx反向代理做同样的事。这样前端代码里不需要写死后端地址部署时非常方便。同时这也一举解决了跨域问题跨域是前后端分离项目最常见的第一个坑。5.4 房源详情页从日期选择到订单确认房源详情页是前端交互最复杂的页面也是最能体现“做没做过实际项目”的页面。页面上要有房源轮播图、基本信息、设施列表、房型选择、日期选择、价格计算。日期选择这里有个特殊组件Element Plus的日期范围选择器el-date-picker非常适合但要注意设置disabled-date属性禁止用户选择今天之前的日期。同时日期范围应与房间被预订的日期做联动把已经被预订的日期禁用掉。实现步骤是进入页面时加载该房间的有效日期或已占用的日期数组再传给disabled-date函数。价格计算这里要特别说明民宿的价格体系往往是动态的——同一房间周五周六和周日周一的单价不同。如果做精做细需要一个价格日历表存放每一天的房态和价格如果做简化的课程设计则可以直接用房间表里的price_per_night字段计算总价。前者加分更多后者更省力。我的建议是如果你的答辩时间线允许至少把“价格按日期区间浮动”这个功能逻辑想清楚在文档中说明。前端展示预估总价的代码大致是const nightCount computed(() { if (dateRange.value dateRange.value.length 2) { const start new Date(dateRange.value[0]) const end new Date(dateRange.value[1]) return Math.round((end - start) / (1000 * 60 * 60 * 24)) } return 0 }) const totalPrice computed(() { if (!currentRoom.value) return 0 return nightCount.value * currentRoom.value.pricePerNight })这样用户在点击“立即预订”前就能实时看到总价格体验会好非常多。5.5 管理端页面的常见实现套路管理端页面总体来说是“标准CRUD 表格 弹窗表单”的模式。民宿管理页的字段包括封面图、名称、地址、状态、创建时间、操作按钮。实现上有几个高频坑我说一下第一封面图上传。不能只做文件选择要写一个上传接口由后端保存到本地磁盘或OSS对象存储并返回可访问的URL。如果保存在本地注意配置静态资源映射比如将/upload/**映射到磁盘目录。本地保存方式适合毕设OSS方式更适合真实生产看你需要哪种方案。第二表格的分页。Element Plus的el-table配合el-pagination实现起来不难但是要注意当切换页码时重新请求接口不能只改变前端数据否则一刷新就回到第一页。要在handlePageChange里带上pageNum调接口。第三表单校验。Vue中可以用Element Plus的校验规则注册房源表单至少校验名称、地址、价格、简介不能为空。后端接口的DTO里也要用NotBlank、NotNull这类注解做二次校验。双重校验的原因很简单前端校验是为了用户体验后端校验才是安全底线。6. 项目部署与常见问题排查6.1 本地开发环境版本推荐与初始化环境匹配是这项目最容易卡住新手的第一步。版本太新或太旧都会带来额外麻烦。我推荐一套相对稳定、闭坑的版本组合环境推荐版本说明JDKJDK 8 或 JDK 11Spring Boot 2.x配JDK8最稳若用Spring Boot 3.x建议JDK 17Spring Boot2.7.x资料多、兼容性好适合课程设计和毕设Node.js16.x 或 18.x LTSVite 4以上对Node版本有要求别用太老版本MySQL5.7 或 8.0生产用8.0注意驱动配置不同开发工具IDEA VSCode后端用IDEA前端用VSCode各用所长数据库初始化时记得在application.yml里配置时区和编码spring: datasource: url: jdbc:mysql://localhost:3306/homestay?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: Asia/Shanghai连接MySQL 8.0时注意驱动名是com.mysql.cj.jdbc.DriverMySQL 5.7下则用com.mysql.jdbc.Driver。这个差异也是经典报错点。6.2 前端项目跑通后最容易踩的5个坑端口冲突。Vite默认是5173端口如果你的服务被占用了直接报错Port 5173 is already in use。解决方法有两种关掉占用进程或者配置server: { port: 5174 }换一个端口。跨域问题。前端在http://localhost:5173后端在http://localhost:8080浏览器会拦截不同源的请求。线上可以通过Nginx转发开发时就用Vite代理// vite.config.js server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }这里的/api前缀要跟Axios的baseURL保持一致否则代理会静默失效而且报错信息非常难找。请求拦截器没生效。检查Axios是否用的是同一个封装实例有些组件里单独import axios from axios这会导致拦截器完全不执行登录状态没法统一处理。页面刷新后404。这是用了history模式路由导致的开发环境还好部署到Nginx后刷新非根路径会报404。解决办法是在Nginx配置中加上try_files $uri $uri/ /index.html;或者直接用hash模式。数据回显不对。比如编辑民宿时弹窗表单回显总是失败多半是数据结构不一致——后端返回的字段是homeName前端绑定的字段是name。规范的做法是后端定义VO字段命名严格对齐前端使用场景从源头杜绝这类问题。6.3 后端启动与联调常见问题速查后端启动失败90%的概率出在数据库连接上。第一个要检查的是MySQL服务是否真的启动了第二个是用户名密码是否正确第三个是数据库名字是否存在。新手常犯的错误是项目里配置的库名homestay还没在MySQL中创建直接启动当然就报Unknown database。启动成功但接口404优先检查Controller是否扫描到。Spring Boot启动类的SpringBootApplication默认扫描同包及子包如果你的控制层包名与启动类不一致或者启动类包名层级比Controller包层级高接口就会找不到。Postman测试接口时中文乱码检查两处后端characterEncodingutf8是否配置请求头是否设置了Content-Type: application/json同时JSON里包含中文时要注意字符编码。接口返回500先看IDEA控制台的异常堆栈常见三类SQL语法问题、空指针、参数类型转换失败。找到第一行报错信息才能定位是哪一层的问题。6.4 数据库字段设计失误的补救方式做项目过程中最痛苦的事情是做到后期突然发现表结构设计不合理要回炉重改。我吃过的亏是订单表最开始没设计order_no字段导致支付回调无法很好地追踪订单后来又要补数据。所以建表前一定要多花时间想清楚哪些字段是刚需哪些是后期扩展要预留的。如果你已经开始写了发现表设计确实有问题我的建议是只要项目还没正式部署就直接改表结构不要用SQL打补丁。同时启用spring.jpa.hibernate.ddl-autoupdate这样的自动更新模式还是会有风险保险的做法是手动写ALTER语句并且同步修改实体类。这里有两条经验一是不要过度设计比如为了以后可能有的促销活动就提前设计“优惠券表”。这个不属于民宿管系统的核心业务容易让设计文档变得臃肿答辩时反而被问住。二是像“预订状态”这类状态字段不要一开始就设计十几个状态增加实现成本。0到5的状态流转对应好每一步触发逻辑写清楚就行。7. 扩展方向与个人实操心得7.1 已经跑通的系统还可以怎么升级如果项目做完了还有精力我建议优先做三个方向的扩展性价比很高。第一个方向是图表统计。管理端做一个统计看板按月份统计订单数量和营收用ECharts或Vue版的vue-echarts渲染柱状图和折线图。这个功能既好看又能体现MySQL分组聚合能力后端只需要写一条GROUP BY DATE_FORMAT(create_time,%Y-%m)的SQL是技术和展示双加分的方向。第二个方向是支付对接。可以接入支付宝沙箱或模拟支付流程走真实支付中非常关键的“回调验签”逻辑。民宿系统里订单状态从“待支付”到“已支付”的推动靠的就是支付通知后更新订单。这个功能的一个直观意义在于做完它你就算真正摸过业务的闭环了。第三个方向是消息通知。当用户下单成功、房东确认订单、订单即将到期时发站内信或邮件通知。技术实现不复杂设计一张通知表然后在下单逻辑的合适位置插入通知记录前端个人中心展示未读数量——这个功能能让“个人中心页”立刻丰满起来。7.2 我做这个项目时踩过的几个坑这个部分我想跟你分享几点真实感受因为很多教程不会写这些。第一不要把大量时间花在配置环境上。我当时为了把Spring Boot版本从2.3升到2.7折腾了半天后来发现新版本稳定是稳定但网上很多教程的写法都不适配了反而踩了不少坑。毕设项目选一套主流版本组合从一而终不要中途更换。第二前后端联调的时间要预留充足。很多新手把后端接口写完、前端页面写完就以为大功告成等真正联调时发现字段对不上、类型不一致、日期格式不匹配一连串问题扑面而来。我的建议是先定一个字段命名与类型清单前后端都按这个清单来开发能少浪费很多时间。第三写代码时给自己留“答辩功能点”。比如防超卖的原子更新、JWT认证、全局异常处理、VO和DTO分层这些点首先是为了系统本身能在真实场景下正常运作同时也是你答辩时可以展开讲、能讲出技术深度的素材。别让项目只停留在“页面能访问、增删改查能跑”的层次。最后想说的是民宿管理系统这个题目表面上是做一个CRUD系统但对认真做完它的人来说相当于完整经历了一个前后端分离项目从零到一的全过程。把这张工程的网织好后面再去做电商、餐饮、社区类系统你都会发现路是通的。别赶进度给自己留够调试和思考的时间每解决一个报错都是实打实地往前走了一步。