ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于Django的酒店管理系统:从数据模型到订单闭环的全栈实践

基于Django的酒店管理系统:从数据模型到订单闭环的全栈实践 简介基于Python Django框架的伊人酒店管理系统设计与实现文档面向计算机专业学生、Web开发入门者及中小型酒店管理人员。文档完整覆盖系统需求分析、可行性分析、UML建模、功能模块设计、数据库设计及网络接口设计等内容重点阐述了前后端分离开发、MySQL数据库、Django ORM及Vue.js渲染等关键环节能帮助读者快速理解Web酒店管理系统的完整实现路径。资源为1个docx文件压缩包约2.37MB便于阅读和打印。目前已有71人学习。对于课程设计或毕业设计人群此文档提供了清晰的设计思路、模块化开发方法和详细用例规约参考其体系结构可显著减少系统设计阶段的重复劳动是一份实用性较强的参考资料。1. 酒店管理系统自研的起点从需求乱麻到技术选型市面上的酒店管理系统不少但绝大多数是给星级酒店设计的功能堆得很满中小型酒店买回去发现一半模块用不上员工培训成本还高。这套基于 Python Django 的伊人酒店管理系统走的是另一条路只做真正高频的流程把“官网展示 在线预订 后台管理”拆成两个客户端让住客在前台订房、管理员在后台管房。拆解这套系统的意义不只是看代码而是看它如何用 Django 的 ORM 把房间、价格、订单、消息这些实体串成一个完整的业务闭环。对于想上手 Django 全栈开发的人或者需要给中小商户做管理系统的开发者这套设计的模块划分和数据表结构都有直接可抄的价值。2. Django 数据模型设计房间、价格与订单的实体关系拆解2.1 核心实体的建模思路酒店管理系统本质上在管理三类核心数据房间资源、价格策略、订单流转。伊人系统把这三大块拆成了清晰的 Django model没有过度设计但每一张表都有明确的业务含义。先看最基础的房型和房间号设计from django.db import models class RoomType(models.Model): name models.CharField(max_length50, verbose_name房间类型名) price models.DecimalField(max_digits8, decimal_places2, verbose_name默认价格) description models.TextField(verbose_name房间描述) free_services models.TextField(verbose_name免费服务) cover_image models.ImageField(upload_toroom_type/, verbose_name封面图) is_active models.BooleanField(defaultTrue, verbose_name是否启用) class Meta: db_table room_type class Room(models.Model): room_type models.ForeignKey(RoomType, on_deletemodels.CASCADE, verbose_name所属房型) room_no models.CharField(max_length10, uniqueTrue, verbose_name房间号) is_active models.BooleanField(defaultTrue, verbose_name是否启用) class Meta: db_table room这里把“房间类型”和“房间号”分成两张表是酒店业务里很重要的一个设计决策。一个房型对应多个房间比如“豪华大床房”这个类型下有 101、102、103 三个房间。后台管理时管理员先维护房型再往房型下挂房间号添加房间号时强制选择所属房型。用ForeignKey建立一对多关系db_table显式指定表名方便后期 DBA 接手时直接看表结构。值得注意的细节是is_active字段几乎出现在每张业务表里。它不是冗余而是给管理员留了一个“软开关”——某个房型要停售不需要删数据直接关掉启用状态就行历史订单仍然能正确关联到房型信息。2.2 价格日期表解决“某一天调价”的痛点酒店价格的典型场景是默认价格是 500 元一晚但国庆期间某天要涨到 800 元。如果直接在房型表上改价格改完还要改回来既不灵活也容易出错。伊人系统单独设计了一张价格日期表来处理这个问题class RoomPriceDate(models.Model): room_type models.ForeignKey(RoomType, on_deletemodels.CASCADE, verbose_name房型) date models.DateField(verbose_name日期) price models.DecimalField(max_digits8, decimal_places2, verbose_name当日价格) class Meta: db_table room_price_date unique_together (room_type, date)这张表的含义是某房型在某天的特殊价格。查询某天价格时优先查RoomPriceDate查不到就回退到RoomType.price默认价。unique_together保证了同一个房型在同一天只有一条价格记录从数据库层面防止脏数据。前端预订页面展示 30 天价格日历就是循环查这张表的结果集把有特殊价格的日期标出来。2.3 订单表与状态流转订单是整个系统的核心枢纽串联起用户、房间、价格、入住天数、增值服务这些信息。伊人系统的订单模型设计覆盖了前台用户自助下单和管理员后台代下单两种场景class Order(models.Model): ORDER_STATUS_CHOICES ( (pending, 待入住), (checked_in, 已入住), (checked_out, 已退房), (cancelled, 已取消), ) user models.ForeignKey(User, on_deletemodels.CASCADE, nullTrue, blankTrue, verbose_name下单用户) room_type models.ForeignKey(RoomType, on_deletemodels.CASCADE, verbose_name房间类型) room models.ForeignKey(Room, on_deletemodels.CASCADE, verbose_name房间号) check_in_date models.DateField(verbose_name入住日期) check_out_date models.DateField(verbose_name离店日期) nights models.IntegerField(verbose_name入住天数) total_price models.DecimalField(max_digits10, decimal_places2, verbose_name总价) contact_name models.CharField(max_length20, verbose_name联系人) contact_phone models.CharField(max_length11, verbose_name联系电话) status models.CharField(max_length20, choicesORDER_STATUS_CHOICES, defaultpending) created_at models.DateTimeField(auto_now_addTrue)订单状态用字符串常量加choices限定比用数字状态码直观得多。Django admin 后台会渲染成下拉框不会出现填错状态值的情况。关键设计是user字段允许为空因为后台管理员代客人下单时客人可能没有注册官网账号这时候订单仍然能正常创建只是关联不到 user。2.4 消息与增值服务的扩展设计伊人系统的留言和增值服务模块值得单独拎出来说。评论和投诉共用一张消息表用type字段区分是“普通评论”还是“投诉”后台消息管理界面通过这个字段做筛选。增值服务模块拆成导游管理、合作酒店管理、景点协调管理三块导游信息里带折扣字段合作酒店带链接字段景点带门票折扣字段。这三个实体本质上都是“给酒店引流或增收的外部资源”做成三个独立 model 比合并成一张多态表更符合 Django 的惯例——查询简单字段类型清晰admin 注册后直接获得增删改查能力。3. 双端功能实现门户网站与管理后台的业务闭环3.1 门户网站预定流程的状态机与视图函数门户网站面向住客入口是酒店首页。首页展示酒店简介和周边景点访客选择入住日期和离店日期后点击搜索系统将日期参数传给预定页面返回该时间段内所有可预订的房型def search_available_rooms(request): check_in request.GET.get(check_in) check_out request.GET.get(check_out) available_types [] room_types RoomType.objects.filter(is_activeTrue) for room_type in room_types: # 排除日期范围内已锁定的房间 conflicting_orders Order.objects.filter( room_typeroom_type, status__in[pending, checked_in], check_in_date__ltcheck_out, check_out_date__gtcheck_in ) total_rooms Room.objects.filter(room_typeroom_type, is_activeTrue).count() locked_rooms conflicting_orders.values(room).distinct().count() if locked_rooms total_rooms: available_types.append(room_type) return render(request, booking.html, {available_types: available_types})参数check_in和check_out由前端日期选择器格式化后拼到 URL 上后端用request.GET.get()接收。判断房间是否可订的核心逻辑是区间重叠检测已存在订单的入住日期在当前查询的离店日期之前且订单的离店日期在当前查询的入住日期之后说明时间有交集该房间在这个区间内不可用。用distinct().count()统计被锁定的房间数再对比该房型下总房间数就能算出还有没有余房。这种校验方式没有依赖复杂 SQL纯 Django ORM 就能完成对中小规模酒店完全够用。用户点击“立即预定”后进入订单确认页。如果未登录系统拦截请求并重定向到登录页登录完成后带参数跳回预定页。提交订单时用 Django Form 做字段校验重点验证手机号格式和入住人姓名字段非空。3.2 管理后台30 天房间状态看板的实现管理后台是伊人系统的核心其中最有看点的功能是“房间状态管理”——展示当日起 30 天内每一个房间每一天的预定状态管理员点击某天的某个房间格可以直接弹窗创建订单、办理入住或退房。这个功能直观上是个日历热力图实现上核心是对二维数据的组织横轴是 30 个日期纵轴是每个房间号。视图层组装数据的方式如下def room_status_board(request): today timezone.now().date() date_list [today timedelta(daysi) for i in range(30)] rooms Room.objects.filter(is_activeTrue).select_related(room_type) # 获取30天内的所有订单按房间和日期建立索引 orders Order.objects.filter( status__in[pending, checked_in], check_out_date__gttoday, check_in_date__lttoday timedelta(days30) ) status_map {} for order in orders: current max(order.check_in_date, today) while current order.check_out_date and current today timedelta(days30): status_map[(order.room_id, current)] order.status current timedelta(days1) board_data [] for room in rooms: row {room: room, statuses: []} for date in date_list: row[statuses].append(status_map.get((room.id, date), available)) board_data.append(row) return render(request, admin/room_status.html, {board_data: board_data, date_list: date_list})这里用(room_id, date)作为字典键把订单状态铺平到日期维度上避免了在模板里做嵌套循环查库。每个房间每天只会命中一个状态available、pending 或 checked_in。页面渲染时不同的状态映射到不同的 CSS 颜色管理员一眼就能看出哪些房间空着、哪些已订出、哪些今天退房。点击事件用 JavaScript 绑定把房间 ID 和日期传给弹窗弹窗里再加载订单创建表单。3.3 价格修改与实时刷新房间价格管理页面与状态看板类似也是 30 天日历视图但数据源自RoomPriceDate表。管理员点击某天的价格弹出输入框提交后走下面的逻辑def update_price(request, room_type_id): if request.method POST: date request.POST.get(date) price request.POST.get(price) RoomPriceDate.objects.update_or_create( room_type_idroom_type_id, datedate, defaults{price: price} ) return JsonResponse({code: 0, msg: 价格已更新})update_or_create是 Django ORM 里很实用的一个方法——当天该房型已有特殊价格记录就更新没有就创建一步完成。前端页面无需刷新Ajax 提交后返回 JSON前端把页面上的价格文本直接替换成新值。这套交互做下来整个价格调整流程顺畅得跟操作 Excel 一样完全不需要去改数据库。4. 关键业务逻辑落地的坑与对策订单冲突、折扣计算与消息联动4.1 订单创建时的并发冲突处理管理后台代客人下单时两个前台同时操作同一个房间同一天很可能出现重复售卖。伊人系统在订单创建时加上了一层乐观锁保护def create_order(request): room_id request.POST.get(room_id) check_in request.POST.get(check_in) check_out request.POST.get(check_out) nights (datetime.strptime(check_out, %Y-%m-%d) - datetime.strptime(check_in, %Y-%m-%d)).days # 再次校验房间在目标区间是否可用 conflict Order.objects.filter( room_idroom_id, status__in[pending, checked_in], check_in_date__ltcheck_out, check_out_date__gtcheck_in ).exists() if conflict: return JsonResponse({code: 1, msg: 该房间在所选日期已被预订}) # 通过校验后创建订单这段代码出现在用户提交订单和管理员代下单两个入口。前端已经过滤过可订房型但后端必须再做一次校验因为前端展示的数据可能已经过期直接信任前端传参很容易产生超卖。订单创建后房间状态看板下一次加载时会自动把新订单纳入锁定区间。4.2 增值服务与导游折扣的订单联动导游订房场景下导游带团入住需要按协议折扣结算。伊人系统的做法是订单创建弹窗里提供一个“选择导游”的下拉框选中后自动带出该导游的折扣比例前台工作人员手动确认总价def calc_discounted_price(room_type_id, date_list, discount, extra_services): total 0 for date_str in date_list: date datetime.strptime(date_str, %Y-%m-%d).date() price_obj RoomPriceDate.objects.filter(room_type_idroom_type_id, datedate).first() if price_obj: total price_obj.price else: total RoomType.objects.get(idroom_type_id).price total * discount for service in extra_services: total service.price return round(total, 2)总价计算分三段先按日期逐天取价——当天有特殊价格用特殊价没有用默认价再乘以折扣——导游折扣和景点合作的入住的折扣都走这个逻辑最后叠加增值服务费用。计算完返回给前端展示管理员确认无误后提交订单。折扣比例没有单独存到订单表因为导游和景点的合作协议可能会调整存比例快照反而更合理——不过伊人系统是当下单时手动算好总价直接存进订单金额字段后续协议变更不影响已生成订单。4.3 评论与投诉的消息联动机制门户站用户订单结束后可以发表评论或投诉后台消息列表会展示这些内容。这里的联动逻辑是用户在个人中心看到订单状态变为已退房点击“评价”按钮选择“好评/差评”或“投诉”提交后写入消息表。后台管理员回复后门户站对应评论下方会显示回复内容同时用户的个人中心收到一条未读消息。这个消息联动用 Django 的信号机制可以优雅实现在Message模型的save方法里判断是否被管理员回复过class Message(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name发布者) content models.TextField(verbose_name内容) reply models.TextField(blankTrue, nullTrue, verbose_name管理员回复) is_complaint models.BooleanField(defaultFalse, verbose_name是否为投诉) created_at models.DateTimeField(auto_now_addTrue) def save(self, *args, **kwargs): is_new self.pk is None super().save(*args, **kwargs) if not is_new and self.reply: # 向评论发布者发送未读消息通知 Notification.objects.create(userself.user, content您提交的反馈已收到回复)save方法重写时有个容易忽略的细节必须在super().save()之后才能创建通知因为在调用super()之前主键还没有生成。这里仅讨论了核心思路实际开发中也可以把通知逻辑放到视图层处理。5. 系统测试用例设计与 Django 项目的本地部署验证5.1 功能测试用例的组织方式伊人系统的测试覆盖分为两块第一块是业务流程测试第二块是权限控制测试。业务流程测试的核心用例如下测试模块测试操作预期结果门户网站预定用户登录后选择日期和房型提交订单订单创建成功管理后台状态看板对应房间日期变为已预订管理后台代下单管理员点击空闲房间格填写入住信息提交新订单生成状态看板实时更新价格动态调整管理员修改某天某房型价格门户网站预定界面对应日期价格同步更新评论投诉联动用户提交评论管理员回复门户网站显示回复内容用户个人中心收到未读消息增值服务折扣管理员选择导游后录入订单订单总价按导游折扣比例自动计算权限控制这块的测试重点是普通官网注册用户直接访问管理后台地址时应该被拦截并提示无权限管理员账号被禁用后无法登录普通管理员尝试添加新管理员账号时操作被拒绝。这些逻辑在 Django 中可以用user_permissions或自定义装饰器实现测试时验证的是装饰器正确拦截了未授权访问。5.2 Django 项目的本地运行与调试拿到系统源码后本地跑起来只需要几步。项目开发环境是 PyCharm虚拟环境管理是标准的venv方式# 1. 创建并激活虚拟环境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 2. 安装项目依赖 pip install -r requirements.txt # 3. 初始化数据库 python manage.py makemigrations python manage.py migrate # 4. 创建超级管理员账号 python manage.py createsuperuser # 5. 启动开发服务器 python manage.py runserver 0.0.0.0:8000makemigrations会扫描所有 app 下的models.py把模型变更生成迁移文件migrate将迁移文件应用到 MySQL 数据库。项目生产环境用的是 MySQL本地调试如果没装 MySQL可以把settings.py里的数据库配置临时改成 SQLite改一行代码的事——这在前期快速验证页面效果时很省事。runserver启动后浏览器访问http://127.0.0.1:8000进入酒店官网访问http://127.0.0.1:8000/admin进入 Django 自带后台再访问系统自定义的管理端 URL 需要先登录管理端账号。5.3 生产部署的注意点环境变量与静态文件开发环境跑通后部署到 Linux 服务器有几个常见坑。DEBUG必须设为False否则会暴露完整报错堆栈和配置信息ALLOWED_HOSTS必须填上实际域名或服务器 IP不然 Django 会拒绝请求静态文件需要执行python manage.py collectstatic统一收集否则前端页面样式全部丢失。数据库配置建议通过环境变量读取不要写死在settings.py里方便不同环境切换。项目技术栈中提到了 Nginx部署时可以用 Nginx 托管静态文件并反向代理到 Django 应用这套方案比直接用runserver扛生产流量可靠得多。6. 把伊人酒店系统抽成可复用的 Django 业务骨架伊人酒店系统最有价值的不是某个页面写得多漂亮而是它把“资源—时间—订单”三者的关系梳理清楚了。把它抽象成一套可复用的业务骨架任何需要按时间维度管理可售资源的场景——民宿预订、会议室租赁、共享工位、甚至车辆调度——都可以直接套用这套数据模型。房型表对应资源类型表房间号表对应具体资源实例表价格日期表对应动态定价表订单表对应占用记录表。30 天状态看板本质上就是一个“资源 × 日期”的二维网格这套视图逻辑在管理后台可以直接复制到任何 Django 项目中。具体复用的时候注意三个边界条件。第一时间粒度的选择——伊人系统以天为最小单位但会议室租赁可能需要按小时计价这时把RoomPriceDate.date字段改成DateTimeField即可状态看板的横轴从 30 天变成一天内的 24 小时。第二订单状态枚举需要根据业务扩展——租赁场景可能需要“已支付待使用”“使用中超时”“已完成待评价”等状态直接在choices里扩充即可。第三价格计算逻辑中折扣叠加的优先级要提前约定——伊人系统是固定折扣乘以总价如果业务有满减、会员折扣、限时优惠叠加建议把计价逻辑单独抽成PricingService类不要把计算逻辑散落在视图函数里。这套骨架的取舍是放弃了通用性换取开发效率拿到手改改模型字段就能跑业务对中小型团队来说性价比很高。本文还有配套的精品资源点击获取
返回列表