Flask搭建的酒店预订系统源码:含前台订房+后台管理完整功能

发布时间:2026/6/6 14:50:30

Flask搭建的酒店预订系统源码:含前台订房+后台管理完整功能 本文还有配套的精品资源点击获取简介用Python Flask开发的轻量级酒店客房管理系统直接可运行。用户能查房型、选入住退房日期、提交订单、实时查看订单状态管理员可维护房间信息增删改查、审核订单、登记入住、查看统计图表。代码按MVC分层组织models里是SQLAlchemy定义的数据模型views负责路由和页面响应controller封装核心业务逻辑service提供通用方法如密码加密、时间处理templates用Jinja2渲染页面static存放CSS、JS和图片config统一管理数据库和调试配置启动脚本是start.py。配套utils工具类包含表单验证、会话处理等常用功能。系统内置基础权限区分用户/管理员和输入校验所有模块注释清晰目录结构规范适合课程设计实践或小型酒店快速上线使用。1. 这不是Demo是能真正在小酒店跑起来的预订系统我带过六届高校Python课程设计每年都有学生交“酒店预订系统”——结果八成是首页一个轮播图、中间三张房型卡片、底部写着“敬请期待”。直到去年暑假本地一家连锁民宿老板找到我说想给三家店配个轻量系统不要SaaS那种按年付费、改个字段要等客服排期的就要能自己改代码、加字段、连打印机、导Excel的那种。我翻出这套Flask酒店系统源码只花了三天就部署上线现在他们前台用iPad点几下就能完成入住登记老板手机微信里看日报表客房经理每天早上十点自动收到空房清单邮件。它没有炫酷的大屏可视化但每行代码都踩在真实业务痛点上比如用户选日期时系统会实时过滤掉已被预订或正在清洁的房间管理员审核订单时点击“通过”按钮的瞬间不仅状态变更还会自动触发短信模板生成并推入队列入住登记完成后系统立刻把该房间从可预订列表中摘除并在后台日历视图里标红。这不是教学玩具而是一套经过真实场景淬炼的最小可行产品MVP。核心关键词——Flask酒店系统、客房预订源码、后台管理开发——每一个词背后都是可触摸的模块Flask酒店系统意味着它用原生Flask路由Jinja2模板实现前后端分离不依赖Vue/React学生打开浏览器开发者工具就能看清数据如何从models.Room流到templates/user/room_list.html客房预订源码不是静态页面而是包含完整时间冲突检测逻辑的Python函数比如service/booking_service.py里那个check_room_availability()方法它会把用户选择的入住退房日期转换为日期序列再逐条比对数据库中所有已存在订单的日期区间哪怕只重叠一天也返回False后台管理开发则体现在views/admin_views.py中那些被admin_required装饰器保护的路由以及controller/admin_controller.py里对房间库存的原子性更新操作——当两个管理员同时修改同一间房的房价时系统用SQLAlchemy的with_for_update()锁住记录避免脏写。它适合两类人一是计算机专业大三学生用来理解MVC分层如何真正落地不是教科书上抽象的“Model处理数据”而是models/room.py里Room.status字段如何影响views/user_views.py中/book路由的可用房型筛选二是小型酒店业主或IT运维人员拿来改改logo、换换配色、连上自家打印机第二天就能用。整套系统启动只需python start.py数据库自动建表管理员账号密码写在config.py里明文可见——这不是生产环境的安全实践而是教学与快速验证的务实选择。2. 整体架构设计为什么用Flask而不是Django或FastAPI2.1 选型背后的业务现实考量很多人看到“酒店系统”第一反应是Django——毕竟它自带Admin后台、ORM强大、安全性高。但当我蹲点观察那家民宿的实际工作流后立刻否定了这个方案。他们的前台阿姨平均年龄48岁操作iPad主要靠“点得准、看得清”最怕弹窗提示和复杂表单。Django Admin虽然功能全但默认界面信息密度过高一个房间编辑页挤着30多个字段其中20个是系统自动生成的冗余字段如created_at、updated_at、is_active阿姨根本分不清哪些该填、哪些该忽略。而Flask的轻量特性恰恰成了优势我们完全掌控每个页面的HTML结构在templates/admin/room_edit.html里只渲染6个核心字段——房号、房型、价格、状态、床型、备注其余全部隐藏。更关键的是调试成本Django的中间件链、信号机制、模板继承层级让初学者调试一个404错误要查5个文件而Flask的路由定义直白如app.route(/admin/rooms)对应views/admin_views.py里的def list_rooms()学生跟踪一次请求生命周期从URL输入到数据库查询再到HTML渲染全程不超过200行代码。至于FastAPI它的异步能力在酒店系统里纯属冗余。真实场景中峰值并发不会超过50人三家店同时有客人订房同步IO的瓶颈不在Python层面而在MySQL连接池和网络延迟。强行上异步反而增加学习曲线——学生得先搞懂async def、await、事件循环才能理解为什么service/booking_service.py里一个简单的db.session.query(Room).filter(...)不能直接await。我们选择Flask本质是选择“可控的简单”框架本身不替你做决定所有技术选型都由业务驱动。比如数据库用SQLite起步而非PostgreSQL不是因为性能差而是因为民宿老板第一次部署时我告诉他“不用装数据库软件系统自带”他眼睛一亮——这省去了Windows上配置PostgreSQL服务、设置环境变量、处理权限报错的所有麻烦。后续需要扩展时只需修改config.py里的SQLALCHEMY_DATABASE_URI一行代码切换到MySQL所有模型代码零修改。2.2 MVC分层的真实落地细节这套系统的目录结构看似标准但每个目录下的代码都藏着针对酒店业务的深度定制。先看models/目录这里没有泛泛的User、Order模型而是精准切分业务实体。room.py里Room模型的status字段不是简单的字符串枚举而是用Python的Enum类定义from enum import Enum class RoomStatus(Enum): AVAILABLE available # 可预订 OCCUPIED occupied # 已入住 CLEANING cleaning # 清洁中 MAINTENANCE maintenance # 维护中这样做的好处是在views/user_views.py的/rooms路由里查询可用房型时可以直接写Room.query.filter(Room.status RoomStatus.AVAILABLE)语义清晰且类型安全。反观order.py中的OrderStatus则增加了酒店特有的状态流转逻辑class OrderStatus(Enum): PENDING pending # 待审核用户提交后 CONFIRMED confirmed # 已确认管理员通过 CHECKED_IN checked_in # 已入住前台登记 COMPLETED completed # 已完成退房结算 CANCELLED cancelled # 已取消注意CHECKED_IN状态——这是区别于电商订单的关键。当管理员点击“入住登记”按钮时系统不仅要更新订单状态还要联动更新Room.status为OCCUPIED并在service/order_service.py里调用generate_checkin_report()生成PDF入住单。这种强业务耦合正是MVC分层的价值所在models只管数据结构定义service封装跨模型的业务规则views专注HTTP协议交互。再看controller/目录这里存放的是真正的“指挥中心”。比如user_controller.py里的process_booking()方法它不直接操作数据库而是协调多个servicedef process_booking(user_id, room_id, check_in, check_out): # 1. 调用booking_service检查房间可用性 if not booking_service.check_room_availability(room_id, check_in, check_out): raise BookingConflictError(房间在指定日期已被预订) # 2. 调用payment_service生成预授权模拟 payment_id payment_service.create_preauth(user_id, calculate_price(room_id, check_in, check_out)) # 3. 调用order_service创建订单事务内 order order_service.create_order(user_id, room_id, check_in, check_out, payment_id) return order这种设计让业务逻辑可测试、可复用。当需要接入真实支付网关时只需重写payment_service.py所有调用方代码无需改动。最后是views/目录它严格遵循RESTful风格但做了酒店场景的适配。比如/api/orders/int:order_id/checkin这个API不是简单的状态更新而是包含完整的入住校验检查订单是否为CONFIRMED状态、检查入住日期是否早于今天、检查房间当前状态是否为AVAILABLE。这些校验逻辑如果堆在view函数里会臃肿不堪所以被提取到controller/checkin_controller.py中view层只做薄薄一层胶水代码。这种分层不是为了炫技而是让每个模块的修改边界清晰——当老板说“我要在入住单上加个客人身份证号字段”我只需要改三处models/order.py加字段、templates/admin/checkin_form.html加输入框、controller/checkin_controller.py加参数校验绝不会牵一发而动全身。3. 核心功能模块解析从订房到入住的全流程拆解3.1 用户端订房流程时间冲突检测的实战实现用户订房看似简单选房型、选日期、点提交。但背后的时间冲突检测是整个系统的技术难点。很多学生写的“预订系统”只检查订单表里是否有同房间的记录却忽略了酒店运营的真实约束一间房在A客人入住期间1月1日-1月5日不可预订但1月6日清洁后即可接受新订单若B客人订了1月5日-1月8日则系统必须拒绝因为1月5日存在“入住日重叠”。这套源码的解决方案藏在service/booking_service.py的check_room_availability()函数里def check_room_availability(room_id, check_in, check_out): 检查房间在指定日期范围内是否可用 :param room_id: 房间ID :param check_in: 入住日期date对象 :param check_out: 退房日期date对象 :return: True表示可用False表示冲突 # 步骤1获取该房间所有已存在的订单排除已取消的 existing_orders Order.query.filter( Order.room_id room_id, Order.status.in_([OrderStatus.CONFIRMED, OrderStatus.CHECKED_IN, OrderStatus.COMPLETED]) ).all() # 步骤2将入住退房日期转换为日期集合 requested_dates set() current check_in while current check_out: requested_dates.add(current) current timedelta(days1) # 步骤3遍历每个现有订单检查日期交集 for order in existing_orders: # 订单的入住退房日期构成一个日期集合 order_dates set() date_cursor order.check_in while date_cursor order.check_out: order_dates.add(date_cursor) date_cursor timedelta(days1) # 如果两个日期集合有交集则冲突 if requested_dates order_dates: return False return True这段代码的关键在于“日期集合交集”判断。它比常见的“区间重叠公式”max(a_start, b_start) min(a_end, b_end)更直观可靠尤其处理跨月、闰年等边界情况时不易出错。实测中当用户选择2024年2月28日入住、3月2日退房共4天系统会生成集合{2024-02-28, 2024-02-29, 2024-03-01}然后与所有现存订单的日期集合比对。若发现某订单覆盖2024-03-01则立即返回False。这个函数被views/user_views.py的/book路由调用失败时返回JSON{ success: false, message: 所选日期房间已被预订 }前端用SweetAlert弹窗友好提示。更进一步我们在templates/user/room_detail.html里实现了“动态日期过滤”当用户在日历控件中选择入住日期后JavaScript会发起AJAX请求到/api/rooms/room_id/available_dates?check_in2024-03-01后端调用同一check_room_availability()函数但只传入check_in参数计算未来30天内所有可预订的退房日期返回一个日期数组前端据此禁用日历中不可选的日期。这种前后端协同让用户体验从“提交后报错”升级为“实时预防错误”。3.2 后台管理模块权限控制与数据统计的工程实践管理员后台不是简单的CRUD界面而是围绕酒店运营核心动作构建的工作台。views/admin_views.py里的路由设计直指业务痛点/admin/dashboard首页仪表盘显示今日预订数、今日入住数、空房数、待审核订单数。数据来自service/statistics_service.py的聚合查询使用SQLAlchemy的func.count()和group_by()避免N1查询。/admin/rooms房间管理页支持按状态可用/已入住/清洁中、房型、价格区间筛选。关键创新在于“批量状态更新”勾选多间房下拉选择“设为清洁中”一键触发Room.update().where(Room.id.in_(ids)).values(statuscleaning)比逐条更新快10倍。/admin/orders/pending待审核订单页每条记录旁有“通过”、“拒绝”按钮。点击“通过”时后端执行原子操作更新订单状态为CONFIRMED同时插入一条系统日志到models/log.py并触发notification_service.send_sms()发送确认短信模拟。权限控制采用装饰器模式定义在utils/auth.pydef admin_required(f): wraps(f) def decorated_function(*args, **kwargs): if user_id not in session: return redirect(url_for(auth.login)) user User.query.get(session[user_id]) if not user or user.role ! admin: flash(权限不足请使用管理员账号登录, error) return redirect(url_for(auth.login)) return f(*args, **kwargs) return decorated_function这个装饰器被应用在所有/admin/*路由上但注意它不处理“用户只能看自己的订单”这类细粒度权限——那是controller/order_controller.py的责任。比如/admin/orders/int:order_id路由会先调用order_controller.get_order_by_id(order_id)该方法内部检查current_user.role admin才返回订单详情否则抛出403异常。这种粗粒度细粒度的双重防护既保证入口安全又防止越权访问数据。数据统计模块是老板最爱的功能。service/statistics_service.py提供三个核心方法1.get_daily_revenue(start_date, end_date)按天聚合收入SQL中用DATE(order.created_at)分组SUM(order.total_amount)求和2.get_room_occupancy_rate(month)计算某月入住率公式为(入住总天数 / (房间数 × 当月天数)) × 100%3.get_top_room_types()统计各房型预订次数用于指导采购决策。这些方法返回字典列表被templates/admin/dashboard.html用Chart.js渲染为折线图、饼图。特别值得一提的是入住率计算——它必须排除CANCELLED订单的天数且只计算CHECKED_IN和COMPLETED状态的订单。我们在models/order.py里添加了is_effective()属性方法property def is_effective(self): 判断订单是否计入入住率统计 return self.status in [OrderStatus.CHECKED_IN, OrderStatus.COMPLETED]这样在统计时db.session.query(func.sum(func.julianday(Order.check_out) - func.julianday(Order.check_in))) \ .filter(Order.room_id room_id, Order.is_effective True)逻辑清晰且可复用。4. 实操部署与调试从零运行到个性化定制4.1 五分钟快速启动指南部署这套系统不需要服务器运维经验Windows/Mac/Linux三端通用。以下是我在民宿现场手把手教前台阿姨助理的操作步骤她只会用鼠标和记事本第一步安装Python环境下载Python 3.9安装包官网python.org勾选“Add Python to PATH”一路下一步。验证按WinR输入cmd回车后输入python --version看到Python 3.9.7即成功。第二步解压并进入项目目录把下载的源码压缩包解压到D:\hotel_system路径不要含中文和空格。打开命令提示符输入cd D:\hotel_system第三步创建虚拟环境并安装依赖虚拟环境是隔离依赖的保险箱避免与其他Python项目冲突python -m venv venv venv\Scripts\activate.bat # Windows # 或 venv/bin/activate # Mac/Linux pip install -r requirements.txtrequirements.txt里只有8个核心包Flask、Flask-SQLAlchemy、Flask-Login、Jinja2、Werkzeug、itsdangerous、click、colorama。没有花哨的AI库全是稳定生产级组件。第四步初始化数据库并启动最关键的一步只需一条命令python start.py此时控制台会输出* Running on http://127.0.0.1:5000 * Debug mode: on用浏览器打开http://127.0.0.1:5000首页出现酒店Logo和房型列表。管理员账号密码在config.py第12行明文写着ADMIN_USERNAME admin ADMIN_PASSWORD hotel123 # 初始密码首次登录后请修改这就是“五分钟启动”的全部操作。没有数据库安装、没有环境变量配置、没有端口冲突排查——所有这些都被封装在start.py里if __name__ __main__: # 自动创建数据库表 with app.app_context(): db.create_all() # 启动开发服务器 app.run(debugTrue, host127.0.0.1, port5000)第五步个性化定制阿姨也能操作老板想换Logo只需替换static/images/logo.png想改首页标语打开templates/base.html找到h1 classtext-3xl温馨如家酒店/h1改成h1 classtext-3xl山居民宿/h1想调整房价进templates/admin/room_edit.html把价格输入框的value{{ room.price }}改成value{{ room.price * 1.2 }}涨20%。所有修改保存后刷新浏览器即生效无需重启服务——这就是Flask开发模式的魅力。4.2 真实调试案例解决“入住登记后房间状态未更新”的坑上周民宿老板打电话“前台说点了入住登记订单状态变了但房间还是显示‘可预订’客人来问能不能订这间房”这是一个典型的事务一致性问题。我远程连接后复现步骤1. 在/admin/orders/pending页点击“通过”某订单 → 订单状态变为CONFIRMED2. 在/admin/orders/confirmed页点击“入住登记” → 订单状态变为CHECKED_IN3. 查看/admin/rooms页该房间状态仍为AVAILABLE问题定位controller/checkin_controller.py的perform_checkin()方法里只更新了订单状态漏掉了房间状态更新。原始代码def perform_checkin(order_id): order Order.query.get(order_id) order.status OrderStatus.CHECKED_IN db.session.commit() # ❌ 只提交了订单修复方案加入房间状态联动更新def perform_checkin(order_id): order Order.query.get(order_id) order.status OrderStatus.CHECKED_IN # 关键修复同时更新房间状态 room Room.query.get(order.room_id) room.status RoomStatus.OCCUPIED db.session.commit() # ✅ 提交订单和房间两个变更但这样还不够健壮。如果db.session.commit()失败如网络中断会出现订单状态变而房间状态不变的不一致。终极方案是使用数据库事务def perform_checkin(order_id): try: order Order.query.get(order_id) order.status OrderStatus.CHECKED_IN room Room.query.get(order.room_id) room.status RoomStatus.OCCUPIED db.session.commit() # 原子性提交 except Exception as e: db.session.rollback() # 出错时回滚所有变更 raise e这个案例揭示了酒店系统的核心原则状态变更必须成对出现。订单状态变CHECKED_IN房间状态必须变OCCUPIED订单状态变COMPLETED房间状态必须变CLEANING。我们在models/order.py里添加了状态变更钩子event.listens_for(Order.status, set) def update_room_status(target, value, oldvalue, initiator): if value OrderStatus.CHECKED_IN and oldvalue ! OrderStatus.CHECKED_IN: target.room.status RoomStatus.OCCUPIED elif value OrderStatus.COMPLETED and oldvalue ! OrderStatus.COMPLETED: target.room.status RoomStatus.CLEANING这样即使其他地方误操作订单状态房间状态也会自动同步形成双重保险。5. 常见问题与避坑指南那些文档里不会写的实战经验5.1 高频问题速查表问题现象根本原因解决方案避坑提示启动时报错ModuleNotFoundError: No module named flask_sqlalchemy虚拟环境未激活pip安装到了全局Python执行venv\Scripts\activate.batWin或source venv/bin/activateMac/Linux再运行pip install -r requirements.txt永远先激活虚拟环境再安装依赖这是Python项目的铁律。可在start.py开头加检测if not hasattr(sys, real_prefix) and conda not in sys.base_prefix: print(警告未检测到虚拟环境)用户登录后无法访问/admin/*页面反复跳转到登录页SECRET_KEY未设置或每次启动随机生成在config.py中固定SECRET_KEY your-secret-key-here不要用os.urandom(24)生产环境必须设置固定SECRET_KEY否则session会话失效。开发时可临时用os.environ.get(SECRET_KEY, dev-key)通过环境变量注入日历控件选择日期后前端AJAX请求返回500错误check_in参数格式错误如2024-03-01T00:00:00带时间戳在views/api_views.py的/api/rooms/room_id/available_dates路由里用datetime.strptime(request.args.get(check_in), %Y-%m-%d).date()强制转为date对象所有日期参数必须清洗前端传来的字符串可能含时区、毫秒后端要用date()方法剥离时间部分管理员修改房价后用户端房型列表价格未更新浏览器缓存了/rooms页面的HTML在views/user_views.py的list_rooms()函数里添加响应头response.headers[Cache-Control] no-cache, no-store, must-revalidate酒店系统必须禁用页面缓存否则价格变动无法实时生效。可在templates/base.html的head里加meta http-equivCache-Control contentno-cache作为兜底5.2 三个血泪教训分享教训一别在__init__.py里初始化数据库实例最初版本我在hotel/__init__.py里写了from flask_sqlalchemy import SQLAlchemy db SQLAlchemy()然后在models/room.py里from hotel import db。这导致循环导入start.py导入appapp导入modelsmodels导入dbdb又试图导入app。正确做法是使用应用工厂模式在create_app()函数内初始化db并通过db.init_app(app)绑定。这个坑我带学生踩了三年直到有个学生用PyCharm的“Find Usages”功能追踪到导入链才破案。教训二日期比较必须用date()不能用datetime有次老板说“周末房价要翻倍”我在service/pricing_service.py里写了if order.check_in.weekday() 5: # 周六日 price * 2结果周一凌晨0点下单的客人check_in是2024-03-04 00:00:00weekday()返回0周一但客人实际是周日晚上入住根源是check_in字段在数据库里是DateTime类型存储了精确到秒的时间。修复方案统一用date()对象比较if order.check_in.date().weekday() 5: # 强制转为date或者更彻底在models/order.py里定义propertyproperty def check_in_date(self): return self.check_in.date()教训三模板继承链不能超过三层为追求“DRY”Don’t Repeat Yourself我把base.html拆成base.html→layout.html→user_base.html→room_list.html。结果某天修改base.html的CSS链接所有页面样式崩溃。调试发现Jinja2的{% extends %}在多层继承时{{ super() }}调用容易错位。最终简化为两层base.html全局布局和user_base.html/admin_base.html角色专属所有页面直接继承对应基模板。模板继承的简洁性永远优于理论上的复用性这是在民宿现场改了17次样式后悟出的道理。6. 后续扩展建议让系统真正长出牙齿这套系统不是终点而是起点。根据民宿老板半年来的反馈我梳理出三条务实的扩展路径每一条都基于真实需求且代码改动可控路径一接入微信公众号消息推送老板抱怨“客人订房后前台要手动打电话确认忙起来就漏接。”解决方案是用微信模板消息替代电话。只需三步1在utils/notification_service.py里新增send_wechat_template()方法调用微信开放平台API2在controller/booking_controller.py的create_order()末尾调用该方法3在templates/admin/wechat_config.html里添加公众号AppID/AppSecret配置表单。所有微信API调用都封装在service/wechat_api.py里用requests.post()发送失败时自动降级为站内信。这个扩展能让90%的确认工作自动化且微信模板消息的到达率远高于短信。路径二增加多语言支持中英双语民宿接待外国游客但当前系统全是中文。Flask-Babel是最佳选择。修改点1在config.py里添加LANGUAGES {zh: Chinese, en: English}2用gettext(Room Type)替换所有硬编码字符串3运行pybabel extract -F babel.cfg -k _l -o messages.pot .生成翻译模板4用Poedit编辑messages.po文件。关键技巧在templates/base.html里加语言切换链接a href{{ url_for(set_language, langen) }}English/a通过session[language]存储用户偏好。这个扩展能让系统无缝服务国际客人且不影响现有功能。路径三对接智能门锁API老板计划采购一批蓝牙门锁希望“客人入住成功后系统自动下发开锁密码”。这需要与硬件厂商合作但软件侧可提前准备1在models/room.py里添加lock_id字段2在service/lock_service.py里预留generate_lock_code(room_id, duration_hours)接口3在controller/checkin_controller.py的perform_checkin()里调用该接口将返回的6位数字密码存入Order.lock_code。当厂商提供SDK后只需实现lock_service.py的具体逻辑。这种“面向接口编程”的设计让硬件升级不再需要重构整个系统。最后分享一个小技巧每次扩展前先在tests/目录下写一个单元测试。比如为微信推送写test_wechat_notification.py用unittest.mock.patch模拟微信API返回确保逻辑正确后再联调。这套系统之所以能在民宿稳定运行半年无故障不是因为代码完美而是因为我们把“可测试性”刻进了每个模块的设计基因里——这才是工程化思维的真正体现。本文还有配套的精品资源点击获取简介用Python Flask开发的轻量级酒店客房管理系统直接可运行。用户能查房型、选入住退房日期、提交订单、实时查看订单状态管理员可维护房间信息增删改查、审核订单、登记入住、查看统计图表。代码按MVC分层组织models里是SQLAlchemy定义的数据模型views负责路由和页面响应controller封装核心业务逻辑service提供通用方法如密码加密、时间处理templates用Jinja2渲染页面static存放CSS、JS和图片config统一管理数据库和调试配置启动脚本是start.py。配套utils工具类包含表单验证、会话处理等常用功能。系统内置基础权限区分用户/管理员和输入校验所有模块注释清晰目录结构规范适合课程设计实践或小型酒店快速上线使用。本文还有配套的精品资源点击获取

相关新闻