
Flask Vue 民宿预约系统的完整开发复盘因为要准备毕业设计我选了一个比较经典的全栈题目——民宿预约系统。一开始预期很乐观觉得不就是房源列表加订单管理吗结果真正动手才发现从表结构设计、房态库存逻辑到前后端联调、部署上线处处都藏着坑。这篇文章把整个项目从零到能跑起来的完整过程写下来包含数据库设计、Flask 后端实现、Vue 前端对接、踩坑排查以及部署答辩的实战经验希望能帮你把规划变成一套真正可运行的民宿预约系统。先交代一下最终的技术选型后端 Python Flask SQLAlchemy数据库 MySQL前端 Vue 3 Vite Pinia Element Plus开发工具 PyCharm接口风格走 RESTful。这套组合在课程设计和毕业设计里非常常见既能锻炼 Python 后端能力又能接触前端工程化整体难度适中复盘价值很高。为什么选 Flask 而不是同类框架这是我在项目启动前反复纠结过的点也会在第一节详细展开。总的原则是民宿预约这种业务规则清晰、状态流转多、需要灵活定制接口的中小型系统Flask 的轻量和可控性优势非常明显。1. 需求边界和技术选型动手写代码前先想明白四件事很多同学做这类项目习惯先打开 IDE 建表写代码结果做到一半发现需求漏洞百出。我的建议是第一步先花一天时间把民宿预约这四个字彻底拆解清楚明确系统到底要管哪些事哪些功能坚决不做。从住客视角看核心流程是搜索房源、查看详情、提交预约、管理订单。这里有兩個容易被忽略的细节。第一预约和支付在课程设计阶段应当分开。绝大多数毕设不会真正接入支付通道所以订单状态至少要有待确认、已确认、已取消、已完成四种而不是简单的下单和删除。第二住客提交预约时选择入住和离店日期这就意味着必须做房态库存管理否则同一间房同一天可能被订两次。从管理员视角看核心流程是房源管理、订单审核、客户管理。民宿和连锁酒店不一样房源往往房型多样——同一个院子可能有单间、标间、整租所以数据模型上要把房源和具体房型分开设计才能支撑后续扩展。我最终给自己定下三个不做的边界不做在线支付只保留订单状态字段方便后续接入不做短信验证登录用用户名加密码注册不做复杂会员积分体系后台只预留字段这三个取舍不是为了偷懒而是让项目聚焦在预约主链路上把核心流程做扎实。这些想清楚后技术选型就顺理成章了。1.1 功能模块拆解前台展示与后台管理系统按角色拆成两个大模块前台展示模块面向住客房源列表页按区域、价格、房型筛选分页展示房源详情页轮播图、设施列表、房型日历、可订状态在线预约选择日期和房型提交预约单订单中心查看我的预约、取消预约、订单状态跟踪登录注册用户名注册、密码登录后台管理模块面向管理员房源管理新增、编辑、上下架房源管理房型、价格、库存订单管理查看预约单、确认或拒绝、处理取消客户管理查看注册用户、禁用异常账号数据看板今日订单数、成交金额按订单金额统计非真实支付、热门房源我最初把评论系统也列进了需求后来主动砍掉了。民宿评论涉及敏感词过滤、回复机制、评分聚合复杂度不低但对预约主流程没有强关联。答辩如果想展示扩展空间把它作为迭代规划讲反而更出彩。1.2 为什么选 Flask 而不是同类重量级框架标题里同时提到了 Flask 和 Django很多同学也卡在这个选型上。我列过一张对比表贴在这里供参考对比维度FlaskDjango上手难度低路由清晰代码量小较高约定多全家桶概念多灵活性高中间件、扩展按需接入中受框架约定限制ORM 支持SQLAlchemy写法自由内置 ORM集成度高后台管理需要自己组装自带 Admin非常省事学习价值能深入理解 HTTP、路由、请求上下文更多是快速构建项目适用场景中小型系统、接口定制多大型系统、快速原型我选 Flask 的核心原因有两个。第一个是业务匹配度高民宿预约的状态流转很复杂订单状态从待确认到已确认再到已完成中间还有取消、拒绝、释放房态等逻辑Flask 的路由和视图写起来轻量直接每一步都能掌控细节。第二个是学习回报率高Flask 让你直观看到请求进来-路由匹配-视图函数处理-返回响应这条主线对打基础非常有用理解了这层后再去接触其他框架会轻松很多。至于 Django如果项目重点是快速搭后台、需求偏行政管理系统那它更合适。两者没有绝对优劣关键看你想要锻炼什么。开发工具我用的是 PyCharm 社区版免费够用。它内置终端、虚拟环境管理、Flask 项目模板调试时能看请求上下文变量对这套开发方式支持得很顺手。2. 数据库设计六张表把民宿预约的地基打好整个项目里我花时间最多的就是数据库设计也是踩坑最多的环节。前端代码可以反复改后端逻辑可以重构但表结构一旦定了后面项目越深入越难动。民宿预约的数据无非三类用户、房源、订单再加上房态、操作记录这些关联数据。最终我设计了六张核心表user用户表存用户名、密码哈希、邮箱、角色、状态property房源表存民宿名称、区域、地址、简介、封面图、设施标签room_type房型表存所属房源、房型名、床型、可住人数、价格、同款房间数booking订单表存用户、房型、入住日期、离店日期、人数、总金额、状态、备注room_inventory房态库存表存房型、日期、已订数量admin_log操作日志表记录管理员关键操作用于审计展示为什么一定要 room_inventory 这张表这是民宿预约和普通电商购物最不一样的地方。商品库存是静态总量卖一个少一个民宿房间的库存是按日期维度的同一间大床房周六可能被订走周日还空着。用一个简单的库存数字完全无法表达这种时间占用关系。所以我设计成房型 日期 已订数量的结构下单时校验目标日期区间的占用情况取消时回补。在业务上这套机制叫房态管理是民宿系统的核心。订单金额我选择在订单表里冗余存储。虽然金额可以通过每晚价格乘以晚数算出来但订单生成后管理员可能改价、做活动折扣实际成交价可能和标准价不同。订单表里直接记录成交金额订单快照的方式能让后续统计不出现歧义。这个思路是从电商项目学来的强烈推荐在类似系统里也这么做。建表时的另一个教训是编码问题。MySQL 5.7 默认字符集是 latin1中文存进去就是乱码。建库时我统一执行CREATE DATABASE homestay_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;字段层面字符串类型也统一 utf8mb4这样中文、表情符号都能正常存储。2.1 用户表与认证方案密码安全和角色控制用户表设计的关键点是密码绝不能明文存储。我用的是 werkzeug.security 中的 generate_password_hash 和 check_password_hash这对函数内部自带加盐哈希安全性足够。登录时用 check_password_hash 比对哈希值而不是比对原密码。角色字段我设计成 role0 代表普通住客1 代表管理员。这里有个实际运营问题管理员账号怎么初始化我用了两种方式配合。第一通过 Flask 的 CLI 命令行创建flask init-admin --usernameadmin --password********第二初始化脚本里预置演示管理员账号方便答辩快速登录。这个初始化接口只允许在本地使用上线前要彻底移除否则任何人都能调用它创建管理员。用户状态字段 status 我设计了 0 正常、1 禁用禁用账号登录时直接被拦截这个字段在后台客户管理模块会用到。关于 token 方案我用 JWT 而不是 Session理由有两点。第一前后端分离架构下Vue 页面和 Flask API 可能运行在不同端口Session 方案需要处理跨域 Cookie、CSRF 防护配置繁琐JWT 通过请求头 Authorization 携带天然适应跨域场景。第二JWT 对小程序、移动端这类后续扩展更友好换端时认证逻辑一点不用改。实现用的是 PyJWT 库登录成功后返回签名 token前端存储后通过 axios 拦截器统一加到请求头。2.2 房型与库存按日计算的可用量模型房型表的核心结构设计成下面这样关键点是 total_rooms 字段它表示该房型有几间同款房CREATE TABLE room_type ( id INT AUTO_INCREMENT PRIMARY KEY, property_id INT NOT NULL, name VARCHAR(50) NOT NULL, bed_type VARCHAR(30), max_guests INT, price DECIMAL(10, 2) NOT NULL, total_rooms INT DEFAULT 1, status TINYINT DEFAULT 1, INDEX idx_property (property_id) );比如一个民宿有两间相同配置的大床房total_rooms 就是 2。对应的 room_inventory 表里每个日期记录 booked_count下单时校验 已订数 本次数量 total_rooms 才允许通过。有一个设计细节值得展开为什么存已订数量而不是剩余数量因为已订数量可以精确回推。取消订单时把对应日期的 booked_count 减回去首次初始化时默认 booked_count0 即可不需要提前生成未来每一天的记录。只有某天第一次被下单才在房态表里插入那天的记录。这个按需生成的思路避免了海量空库存数据查询也快。当然完全按需生成有个问题住客在详情页看到日历时没有记录的日子会显示不可订这不符合预期。所以我的方案是每个房型上架时先初始化未来 90 天的房态数据每天凌晨再用定时任务补上最新一天。开发阶段这个函数手动触发部署后接到系统的 crontab 定时任务即可。2.3 订单表状态流转从待确认到完成的闭环民宿订单和电商订单最大的区别是支付环节通常被人工确认替代。民宿主往往需要和住客沟通、确认房态所以订单状态机比普通购物订单复杂一些。我的状态定义如下0 待确认用户已提交管理员尚未处理1 已确认管理员确认可接待订单生效2 已取消用户或管理员取消3 已完成入住期结束后标记完成4 已拒绝管理员因房态冲突等原因拒绝流转规则是待确认可以流转到已确认或已拒绝已确认可以流转到已完成或已取消已取消和已拒绝是终态不能再变化。这个状态机我集中在一个函数里管理避免各个接口随意改状态产生脏数据。取消动作需要区分发起者。我在订单表加了一个 cancel_by 字段记录是 user 还是 admin 发起的取消。住客取消相对简单管理员取消时如果订单已经是确认状态必须同步释放对应日期的房态。这里我踩过一个很关键的坑值得重点讲最开始实现取消时我先改订单状态再释放房态两个操作不在同一事务里。结果有一次释放房态时数据库报错订单显示已取消但房态还占着数据就乱了。后来把两步包进一个事务用 SQLAlchemy 这样处理with db.session.begin(): order.status 2 release_inventory(order)只要 release_inventory 抛异常整个事务回滚订单状态不会改。这是 Web 开发里非常核心的事务思维尤其是涉及多表变更的业务逻辑必须保证原子性。3. Flask 后端实现从 RESTful API 到房态扣减逻辑后端我用了 Flask 2.x Flask-SQLAlchemy Flask-CORS。项目结构没有照搬官方教程的单文件 app.py而是按模块拆了目录因为功能多了之后单文件维护非常痛苦。我的目录结构参考了工程分层思想homestay_backend/ ├── app.py # 应用入口注册蓝图 ├── config.py # 配置数据库连接、密钥、跨域白名单 ├── extensions.py # db、jwt、cors 等扩展实例 ├── models/ │ ├── __init__.py │ ├── user.py │ ├── property.py │ ├── room.py │ └── booking.py ├── apis/ │ ├── __init__.py │ ├── auth.py # 登录注册接口 │ ├── property_api.py │ ├── booking_api.py │ └── admin_api.py ├── services/ │ ├── inventory.py # 房态查询、扣减、释放 │ └── booking_service.py # 订单创建、状态流转 └── requirements.txtmodels 放数据模型apis 放路由和请求解析services 放核心业务逻辑。很多同学把所有代码堆在 app.py 里能用但后期极难维护。我强烈建议从第一天就按模块分目录哪怕每个文件只有几十行整体清晰度会提升一个档次。3.1 接口设计路由规划与统一返回格式接口风格我选了 RESTful资源用名词复数表示动作通过 HTTP 方法表达。这里是我最终的路由规划GET /api/properties房源列表支持筛选参数GET /api/properties/ 房源详情包含房型和日历库存POST /api/bookings提交预约GET /api/bookings/my当前用户的订单DELETE /api/bookings/ 取消订单POST /api/admin/login管理员登录GET /api/admin/orders订单列表PUT /api/admin/orders/ 处理订单确认或拒绝响应格式全部统一成这种结构{ code: 0, message: success, data: {} }code 为 0 表示成功非 0 表示业务错误。前端做 axios 拦截器统一判断 code非 0 直接弹出错误提示。统一约定避免了前端每个请求单独处理错误分支联调效率高很多。分页参数统一用 page 和 page_size后端返回 total、pages、items 三个字段。不管是房源列表还是订单列表都支持分页避免一次拉全量数据导致前端渲染卡顿。跨域配置是前后端分离项目最容易踩的坑。Vue 开发服务器默认跑在 5173 端口Flask 跑在 5000 端口浏览器会拦截跨域请求。我在 Flask 里用了 Flask-CORS配置允许的来源设为前端地址并允许 Authorization 头。需要注意的是allow_credentials 为 true 时允许来源不能用 *否则浏览器会报错。实际上本地联调时我更推荐用 Vite 的 proxy 方案前端请求 /api 路径由 Vite 开发服务器转发到 Flask 地址。浏览器始终请求同源地址跨域问题在开发阶段就绕开了部署后交给 Nginx 处理同源代理。这个方案更稳少踩跨域的坑。3.2 搜索筛选与日历库存查询高效实现可用房型判断民宿列表页的筛选条件包含关键词、区域、最低价、最高价、入住日期、离店日期。前四个是常规条件拼 SQL 查询即可。麻烦的是日期条件。入住日期和离店日期不是筛选房源的直接条件它们影响的是房型库存。正确的逻辑是用户选择了一个日期区间后只有当某个房型在整个区间的每一天都有余量这个房型才应该出现在结果里。如果对每个房型逐日循环查库存性能会非常糟糕。我的做法分两步第一步按基础条件关键词、区域、价格拿到候选房型 id 集合。 第二步用一条聚合查询检查这些房型在目标日期区间内是否存在已满的日期SQL 大致如下SELECT rt.id, COUNT(*) AS unavailable_days FROM room_type rt JOIN room_inventory ri ON ri.room_type_id rt.id WHERE rt.id IN :room_ids AND ri.date BETWEEN :checkin AND :checkout AND ri.booked_count rt.total_rooms GROUP BY rt.id HAVING unavailable_days 0如果某个房型出现在结果集里说明区间内至少有一天已经满房把它从可用列表排除就是了。一个查询解决了原先 N 次循环才能算完的逻辑数据量上来后也能扛住。日历库存查询则按房型 id 返回未来 90 天的剩余量前端在详情页渲染日历格子。接口响应给前端的数据格式是[ {date: 2025-05-20, remaining: 2}, {date: 2025-05-21, remaining: 0} ]remaining 等于 total_rooms 减去 booked_count。前端根据 remaining 是否大于 0 判断格子是否可点击。需要提醒的是日期格式统一用字符串YYYY-MM-DD尽量避免 datetime 带时区否则部署到服务器后可能因为时区差异出现日期偏移。我在联调时曾遇到本地正常、服务器上日期少了一天的诡异问题排查半天发现就是时区配置后来全部改用 date 类型存储才彻底解决。3.3 预约下单的核心逻辑事务、并发锁与幂等控制预约下单是整个系统风险最高的接口它同时涉及三个关键操作校验库存、创建订单、扣减库存。任何一个步骤出错数据都会不一致。我的实现思路分六步接收参数校验用户输入日期格式、入住人数、房型存在性加锁查询房态记录防止并发下重复下单校验区间内每一天是否可订计算总金额创建订单状态为待确认扣减库存插入或更新房态记录全部成功提交事务失败则回滚第二步是很多人忽略的关键。不加锁的话两个用户同时提交同一房型同一日期可能都通过校验、都扣减库存导致超订。使用 SQLAlchemy 的写法是inventory_rows RoomInventory.query.filter( RoomInventory.room_type_id room_type_id, RoomInventory.date.between(checkin, checkout) ).with_for_update().all()with_for_update() 会对命中行加排他锁第二个事务必须等第一个提交后才能读取。配合 (room_type_id, date) 联合索引行锁扫描效率才有保证。虽然毕设并发量通常不高但这个习惯需要在学校就养成面试聊并发控制时也能讲出实际经验。幂等性我通过 client_token 字段处理。前端在下单请求时生成一个 UUID 传给后端订单表里对 client_token 建唯一索引。如果用户重复点击提交按钮两次请求带同一个 token第二次会被唯一索引挡住。这个方案比单纯前端禁用按钮更可靠因为网络重试也可能导致重复请求后端兜底才是真保障。还有一点不要在事务里再开新事务。SQLAlchemy 的 db.session 本身是会话级事务业务代码里不要在一个还没提交的事务内再调用其他会自行 commit 的函数否则事务边界就会失控。我的经验是核心业务逻辑的提交动作统一放在接口层的最后一步service 层只做查询和修改不主动 commit。4. Vue 前端与 PyCharm 工程化页面组件、状态管理和接口联调前端部分我选了 Vue 3 Vite Vue Router Pinia Element Plus。用 Element Plus 主要因为后台管理页面大量表格、表单、弹窗现成组件能省很多 CSS 时间。前台展示页我主要手写样式配上少量组件避免整站看起来千篇一律。项目结构按页面组织清晰直观homestay_frontend/ ├── index.html ├── vite.config.js ├── src/ │ ├── main.js │ ├── App.vue │ ├── router/index.js │ ├── stores/user.js │ ├── api/request.js │ ├── api/property.js │ ├── api/booking.js │ ├── views/ │ │ ├── Home.vue │ │ ├── PropertyList.vue │ │ ├── PropertyDetail.vue │ │ ├── Login.vue │ │ ├── Register.vue │ │ ├── MyBookings.vue │ │ └── admin/ │ │ ├── Dashboard.vue │ │ ├── RoomManage.vue │ │ └── OrderManage.vue │ └── components/ │ ├── NavBar.vue │ ├── PropertyCard.vue │ └── InventoryCalendar.vueapi 目录按资源模块封装请求函数views 下按路由组织页面组件Pinia 的 store 管理登录状态和用户信息。这套结构对毕设规模完全够用再大一点的工程也能按这个思路扩展。4.1 路由权限控制登录后才能看到页面前端路由权限控制是我联调踩坑比较多的部分。一开始以为后端接口校验就够了后来发现不行如果前端不做路由控制普通用户手动输入管理后台路径虽然接口会拒绝但页面组件会先渲染出来出现一瞬的空白或报错体验差答辩也掉价。我在前端做了两层防护。第一层是路由守卫。在 router.beforeEach 里判断目标路由的 meta.requiresAuth 和 meta.requiresAdmin未登录用户访问受保护页面就重定向到登录页并携带 redirect 参数登录成功后跳回原目标。第二层是接口拦截。axios 响应拦截器统一处理 401、403 状态码TOKEN 失效时清除本地状态并跳转登录页。两层配合后端接口仍然是最强防线前端只是负责提升体验和避免页面闪烁。登录态用 Pinia 管理。我写了一个 user store登录成功把 userInfo 和 token 保存起来。刷新页面时store 会重新读取 localStorage 里的 token并调用获取用户信息接口拉取最新数据。这个刷新恢复登录态的逻辑很容易被忽略很多同学只记得登录时存 token忘记刷新时恢复结果一刷新就变成未登录状态。4.2 民宿详情页与预约表单前后端字段对齐的实战民宿详情页是前后端耦合最深的页面它要展示房源信息、房型列表还要通过日历选择入住和离店日期选完房型后提交预约。这里涉及三个接口房源详情、日历库存、下单。前端处理日历选日期的逻辑是用一个数组保存开始日期和结束日期。选择房型后根据后端返回的 price 动态计算总价。提交预约时请求体要和处理接口的模型严格对齐{ room_type_id: 12, checkin_date: 2025-05-20, checkout_date: 2025-05-22, guests: 2, total_price: 598.00, client_token: uuid-xxx }我要强调一点total_price 不能信任前端后端接单时必须重新计算前端传这个字段只用于辅助比对和记录。任何涉及金额的数据都不能信任前端否则改一下请求就能改价格这是明显的安全漏洞。联调最容易出问题的字段是日期格式和布尔值类型。后端返回的布尔字段可能是 0/1前端直接做条件判断可能踩坑。我在 axios 响应拦截器里对数据做了一层统一处理把日期字段格式化成 YYYY-MM-DD避免后端 datetime 格式带时间部分导致前端校验报错。约定加兜底的思路联调效率能提升不少。4.3 axios 封装与接口联调用最少成本稳定联调axios 封装我在 request.js 里做了三件事配置 baseURL 和超时时间请求拦截器统一加 Authorization 头响应拦截器统一处理业务码和错误提示。const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 0) { ElMessage.error(res.message) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response?.status 401) { // 清除登录态跳转登录页 } ElMessage.error(error.response?.data?.message || 网络异常) return Promise.reject(error) } )这样封装后页面里的请求代码非常干净直接调 api 函数拿数据即可。开发时配合 Vite proxy把 /api 代理到 Flask 的 5000 端口server: { proxy: { /api: { target: http://localhost:5000, changeOrigin: true } } }浏览器请求 /api/properties 时Vite 开发服务器会转发到 Flask前端代码完全不需要关心后端地址。部署到生产环境后Nginx 统一处理反向代理和静态资源前端代码不用改 baseURL这层抽象让开发与部署彻底解耦。5. 开发过程中的真实问题完整排查链路和修复复盘做项目最有价值的不是顺利跑通的代码而是那些让你卡了几个小时、查了很多资料才解决的问题。下面三个问题是我这次开发中印象最深的我会完整还原排查链路而不是直接给答案。5.1 JWT 签名 401 连环坑被复制粘贴的 Bearer 前缀坑惨现象登录成功跳转首页后紧接着调用获取用户信息接口返回 401。这个错误不是每次都出现刷新后特别容易复现。我一开始以为是 token 没存好查 localStorage 明明有值然后又怀疑请求头没加打印拦截器日志也加了再怀疑是签名问题去后端日志里翻果然看到签名验证失败的报错。再仔细查下去发现前端传过来的 token 里有个隐藏问题我在某个地方把 token 存成了带 Bearer 前缀的完整字符串而请求拦截器读取后又加了一次 Bearer 结果变成 Bearer Bearer xxx。后端解析 Authorization 头按空格切分取第二部分时拿到的是 Bearer xxx签名自然校验失败。修复方案是统一 token 的存取规范存储时只存原始 token 字符串请求拦截器统一加 Bearer 前缀。这种前缀重复添加的问题在鉴权体系里太典型了。排查思路其实很简单先看后端日志能不能打出原样的 Authorization 头对比一下就知道问题在哪别靠猜。5.2 库存回补重复执行事务边界不清导致的数据错乱取消订单后需要把房态库存回补回来。最初实现的取消逻辑是修改订单状态再调用释放库存函数。表面看没问题取消后库存确实回来了。但当我连续取消两笔订单时第二笔明显出错——第一笔的库存没真正回补第二笔却多回补了一次。追根溯源问题出现在释放库存函数内部我当时随手写了一句提交事务的代码。这就导致修改状态和回补数据被切成了两个事务一旦第一笔的提交和第二笔的提交交错执行就会出现状态和库存不一致。修复后的逻辑是所有涉及多表修改的操作统一放在一个事务里由最外层统一提交。取消订单函数只做逻辑编排不在中间层提交事务最后在调用方统一处理。排查这个问题的技巧是打开 SQLAlchemy 的日志输出模式让所有 SQL 语句在控制台打印出来开发阶段开着定位事务问题非常方便上线前再关掉避免刷屏。5.3 Element Plus 表格分页与后端分页双重叠加后台订单管理页踩过一个很典型的坑页面数据总是显示不全点击第二页出现数据混乱。原因很简单前端表格自己又做了一层客户端分页而后端接口已经返回分页后的数据两层分页叠加后每页显示条数都对不上。修复方案是明确职责前端完全信任后端分页表格只负责展示 data 数据分页组件绑定 page 和 page_size 并触发重新请求后端负责统一的数据库分页。这类问题排查起来不难从数据条数对不上的现象就能立刻猜到但值得写出来提醒大家联调之前先想清楚分页职责在哪一侧不要在两端各做一遍。6. 部署与演示稳定跑给答辩老师看项目开发完还要能稳定演示。很多同学代码写得不错但部署环节翻车答辩现场打不开页面。这里分享一套适合课程设计的稳妥部署方案。后端用 Gunicorn 作为 WSGI 服务器命令非常简单gunicorn -w 4 -b 127.0.0.1:5000 app:app-w 4 表示开 4 个 worker 进程对毕设的并发量绰绰有余。生产环境用 Nginx 反向代理把 80 端口的请求转发到 Flask 的 5000 端口同时托管前端构建后的静态文件。前端构建npm run build生成的 dist 目录放到 Nginx 的 html 目录下。Nginx 配置里必须加 try_files 规则否则前端 Vue Router 使用 history 模式时刷新页面会 404location / { root /var/www/homestay; try_files $uri $uri/ /index.html; }数据库迁移我用 Flask-Migrate 管理改模型后执行 migrate 和 upgrade避免手工 SQL 改表导致结构漂移。这个工具最初我没装后来改了几次表结构后手工改得十分痛苦装上后省心很多。建议从一开始就引入。答辩演示时演示数据非常关键。我准备了一套完备的演示数据房源图片用真实感强的免费图订单准备不同状态的几条房态日历上故意造一些已满的日期。这样演示时可以直接展示这个房型 5 月 20 日已经满房选别的时间才能预约的场景比临时造数从容得多。部署平台方面毕设场景一台最低配的云主机就够1 核 2G 跑 Flask、Nginx、MySQL 完全没问题。要注意云厂商的安全组端口设置把 80 和 443 放开MySQL 的 3306 端口一定不要对外暴露最好只监听内网地址。7. 往深处走从毕设到可上线产品的扩展方向最后聊聊扩展方向。这部分在答辩和面试时很加分因为体现的是对项目理解的深度和后续规划能力。第一接入真实支付。民宿预约系统接支付本身不复杂真正复杂的是商户资质和回调处理。你可以提前研究统一下单接口的参数、回调验签逻辑、订单关闭流程面试时能把这些流程讲明白就是很大的亮点。第二做小程序端。后端已经是 RESTful API 架构小程序端只需要重新写一套 UI复用现有接口即可。小程序登录体系和现有 JWT 认证如何打通也是一个值得深入的技术点。民宿预约的主力使用场景其实在手机端小程序比 Web 更贴近真实用户。第三扩展为多房东平台模式。现在这套系统相当于一个民宿主自营后台。如果要做成平台模式就需要引入独立的房东角色每个房东管理自己的房源平台做审核和抽佣。这会带来权限模型的大改造引入账户结算、多级分销等复杂逻辑是一个非常有挑战的进阶方向。第四数据分析和智能定价。民宿定价和入住率强相关有了订单历史数据后可以按季节、节假日、周边活动预测需求做动态定价。学生阶段用数据分析工具做简单的数据探索和可视化就能让项目在数据维度上明显超出平均水平。这些都是加分方向不是第一版就要实现的。做项目最重要的永远是把核心链路跑通、把工程细节做好、把接口设计成易扩展的形状。如果你已经跟着这篇文章把民宿预约系统的基础版本跑起来了后面任何一个扩展方向都够你再写一篇长文。最后聊点个人感受。技术选型没有标准答案Flask 也好、Django 也好关键是你是否真正理解了每一行代码为什么这样写。民宿预约系统这个题目胜在业务清晰、链路完整、扩展性好非常适合用来打通前后端开发的任督二脉。我在实际开发中收获最大的其实不是功能都实现的那一瞬间而是一次次排查问题、一次次重构代码的过程。希望这篇复盘能帮你少走一些弯路把时间花在真正有意思的地方。