
简介这是一套开箱即用的酒店管理系统完整源码面向中小型酒店经营者、全栈开发初学者及微信小程序实践者解决传统酒店在订单、房间、餐饮及多端协同管理上的数字化落地难题。资源共1477个文件主体为微信小程序193个wxss、191个json、186个wxml、515个js、Web前端15个html、51个css、40个png/129个jpg等及后台支撑文件95个ts、1个sql等整体包体51.88MB结构清晰模块解耦度高可快速部署调试。目前已有60人学习下载适合希望掌握酒店业务全流程开发、理解微信支付与退款集成、学习多端PC后台H5网站小程序协同架构的开发者。源码已实现房间动态看板、实时消息推送、微信在线下单与订餐、订单全周期管理及标准化支付闭环附带完整静态资源与配置文件无需二次开发即可本地运行验证核心流程。1. 为什么“开箱即用的酒店管理系统”不是噱头而是中小酒店数字化落地的真实拐点你见过太多标着“酒店管理系统”的开源项目首页漂亮文档齐全但 clone 下来跑不起来或者能启动一进订单页就报TypeError: Cannot read property roomType of undefined更常见的是——后台能用网站能看微信小程序编译失败提示wx.request:fail url not in domain list而配置域名又卡在 SSL 证书和备案验证上。这不是你技术不行是绝大多数所谓“全栈酒店系统”根本没过「三端一致性校验」这一关后台 API 接口字段在网站前端被硬编码成room_status小程序却期待status_code推送消息用 WebSocket 实现了但网站没做心跳保活3 分钟断连后用户刷新页面才看到新订单订餐模块的库存扣减逻辑写在小程序端导致并发下单时超卖……这些不是 bug是架构断层。本文讲的是一个真正把「后台管理、PC 网站、微信小程序」三端共用同一套数据模型、同一套权限体系、同一套实时通信通道并预置了真实酒店运营高频场景如房态秒级同步、退房自动释放、多渠道订单聚合的可部署方案。它不追求大而全的 PMS 功能但确保你今天拉下代码、配好数据库、填对小程序 AppID明天就能让前台员工用手机扫码入住、客人在微信里实时查房态、经理在后台看今日餐饮毛利——这才是“开箱即用”的本意省掉联调时间直奔业务价值。2. 三端统一架构设计为什么必须共用一套数据模型与实时通道2.1 数据模型不能“各写各的”必须收敛到一个核心实体图很多团队起步就犯错后台用 Django 写 Room 表字段是room_number,floor,status网站前端 Vue 里定义RoomIteminterface字段却是id,no,level,state小程序再自己搞个RoomInfo加了isCleaned,lastCheckInTime。结果改一个房态字段三端要同步改 7 处。我们采用「单源数据契约」策略所有端共享一份 JSON Schema 描述核心实体由schema/room.json定义{ title: Room, type: object, properties: { id: { type: string, description: 全局唯一ID格式R-2024-XXXX }, roomNumber: { type: string, pattern: ^\\d{3,4}$ }, floor: { type: integer, minimum: 1, maximum: 30 }, status: { type: string, enum: [vacant, occupied, cleaning, maintenance, blocked] }, lastUpdate: { type: string, format: date-time } }, required: [id, roomNumber, floor, status, lastUpdate] }提示这个 schema 不是文档是生成器原料。我们用json-schema-to-typescript为 TypeScript 项目生成接口用djongo的JsonSchemaField在 Django Model 中校验入库数据小程序端用zod做运行时解析。三端拿到的room.status永远是occupied不会出现OCCUPIED或2这类歧义值。2.2 实时推送不是“后台发消息、前端收消息”而是状态驱动的变更广播“实时信息推送”常被简化为 WebSocket 发一条{type: order_new, data: {...}}。但真实场景中一条订单创建会触发连锁反应房态从vacant→occupied、库存扣减、通知保洁排班、向客人发送微信模板消息。若每个环节都独立监听order_new极易漏事件或重复执行。我们采用「状态变更日志State Change Log」模式所有状态变更房态、订单状态、餐品库存都写入state_change_log表结构为字段类型说明idUUID全局唯一变更IDentity_typeVARCHAR(32)room,order,food_itementity_idVARCHAR(64)关联实体ID如R-2024-0001from_stateJSON变更前状态快照仅关键字段to_stateJSON变更后状态快照triggerVARCHAR(32)触发动作如order_create,check_in,inventory_reduce后台服务监听此表通过 Redis Stream 向各端广播变更事件。网站前端用EventSource订阅/api/v1/stream/room-status小程序用wx.connectSocket连接/ws/room收到消息后只做一件事根据entity_id和to_state局部更新对应 DOM 或 WXML 节点。例如收到{ entity_type: room, entity_id: R-2024-1001, to_state: { status: occupied, lastUpdate: 2024-06-15T08:22:31Z } }网站就执行document.getElementById(room-1001).dataset.status occupied小程序调用this.setData({ roomStatus: occupied })。没有业务逻辑只有状态映射——这才是低延迟、高可靠的基础。2.3 权限体系必须穿透三端且支持“场景化细粒度”酒店角色不止管理员、前台、保洁。实际有夜班前台只能操作入住/退房不能改房价、客房主管可查看所有房态但不能处理订单、餐饮部临时工仅能接单、打单不能查看财务。若后台用 RBAC网站用硬编码按钮显隐小程序再写一套权限判断维护成本爆炸。我们采用「策略式权限Policy-based Access Control」所有权限规则定义在policies/目录下如policies/room_checkin.ts// policies/room_checkin.ts export const canCheckIn (user: User, room: Room): boolean { // 夜班前台role night_clerk 且当前时间在 22:00-06:00 if (user.role night_clerk) { const now new Date(); const hour now.getHours(); return hour 22 || hour 6; } // 其他角色需有 check_in 权限 return user.permissions.includes(room:check_in); };该函数被三端复用后台 API 在POST /api/v1/rooms/:id/check-in中调用网站 Vue 组件用v-ifcanCheckIn(currentUser, room)控制按钮小程序在onLoad时调用canCheckIn决定是否显示“办理入住”按钮。权限逻辑只写一次三端行为完全一致。3. 三大版块部署实操从数据库初始化到小程序上线3.1 后台管理端Django Django REST Framework 快速启动我们选用 Django 而非 Node.js核心原因是其 Admin 后台对酒店运营人员极其友好——无需开发即可完成房型配置、价格策略、员工账号管理。部署步骤如下初始化数据库PostgreSQL创建数据库并启用pg_trgm扩展用于房号模糊搜索# 登录 PostgreSQL psql -U postgres CREATE DATABASE hotel_system; \c hotel_system CREATE EXTENSION IF NOT EXISTS pg_trgm;安装依赖并迁移pip install -r requirements.txt # 包含 django, djangorestframework, djongo, channels python manage.py migrate python manage.py createsuperuser # 创建管理员账号启动后台服务含 WebSocket 支持使用 Daphne 替代 Gunicorn以支持 ASGI 协议# 启动 Django Channels WebSocket daphne -b 0.0.0.0:8000 hotel.asgi:application # 同时启动 Celery worker 处理异步任务如微信模板消息发送 celery -A hotel worker -l info参数说明daphne是 Django Channels 官方推荐的 ASGI 服务器比uvicorn更稳定处理长连接celery用于解耦耗时操作避免 WebSocket 连接阻塞。settings.py中CHANNEL_LAYERS配置为 Redis确保多进程间消息互通。3.2 PC 网站端Vue 3 Pinia 实现房态看板与订单聚合网站核心是「实时房态看板」需每秒刷新且不重绘全屏。我们放弃v-for渲染全部房间改用「虚拟滚动 状态快照」!-- views/RoomDashboard.vue -- template div classroom-grid RoomCard v-forroom in visibleRooms :keyroom.id :roomroom status-changehandleStatusChange / /div /template script setup import { ref, onMounted, onBeforeUnmount } from vue import { useRoomStore } from /stores/room import { EventSourcePolyfill } from event-source-polyfill const roomStore useRoomStore() const eventSource ref(null) const visibleRooms ref([]) onMounted(() { // 初始化加载全量房态 roomStore.fetchAllRooms() // 建立 SSE 连接监听房态变更 eventSource.value new EventSourcePolyfill(/api/v1/stream/room-status) eventSource.value.onmessage (e) { const change JSON.parse(e.data) roomStore.updateRoomStatus(change.entity_id, change.to_state) } }) onBeforeUnmount(() { eventSource.value?.close() }) /script逻辑说明useRoomStore是 Pinia store内部用reactive({})存储所有房间对象updateRoomStatus方法直接修改对应房间的status属性触发RoomCard组件响应式更新。SSEServer-Sent Events比 WebSocket 更轻量且天然支持自动重连适合房态这类只读高频推送场景。3.3 微信小程序端原生开发 自研状态同步 SDK小程序不能直接连 WebSocket且微信限制wx.request域名必须备案。我们采用「双通道混合推送」主通道WebSocket使用wx.connectSocket连接/ws/room接收房态、订单状态变更兜底通道HTTP 轮询当 WebSocket 断开时降级为setInterval(() wx.request(...), 5000)获取增量变更。关键封装sdk/sync.js// sdk/sync.js class SyncClient { constructor() { this.socket null this.pollTimer null this.isOnline false } connect() { this.socket wx.connectSocket({ url: wss://your-domain.com/ws/room, header: { Authorization: Bearer ${wx.getStorageSync(token)} } }) this.socket.onOpen(() { this.isOnline true clearInterval(this.pollTimer) console.log(WebSocket connected) }) this.socket.onMessage((res) { const data JSON.parse(res.data) // 统一处理变更更新本地 storage 触发页面事件 wx.setStorageSync(room_${data.entity_id}, data.to_state) wx.$emit(room:update, data) }) this.socket.onError(() { this.fallbackToPolling() }) } fallbackToPolling() { this.isOnline false this.pollTimer setInterval(() { wx.request({ url: https://your-domain.com/api/v1/rooms/changes?since Date.now(), success: (res) { res.data.forEach(change { wx.setStorageSync(room_${change.entity_id}, change.to_state) wx.$emit(room:update, change) }) } }) }, 5000) } } export const syncClient new SyncClient()参数说明wx.$emit是我们注入的全局事件总线基于tiny-emitter页面 onLoad 时监听wx.$on(room:update, callback)即可响应。since参数用于 HTTP 轮询获取增量后台返回last_update_time供下次轮询使用避免全量拉取。4. 避坑指南三端联调中最容易翻车的 5 个致命细节4.1 现象小程序登录成功但后续所有 API 请求返回 401原因后台 Django REST Framework 默认使用 Session 认证而小程序wx.request不携带 Cookie同时未配置CSRF_COOKIE_HTTPONLYFalse导致前端无法读取 CSRF Token。解决弃用 Session 认证改用 JWT。在settings.py中配置REST_FRAMEWORK[DEFAULT_AUTHENTICATION_CLASSES] [rest_framework_simplejwt.authentication.JWTAuthentication]小程序登录后将access_token存入wx.setStorageSync(token, token)所有请求header.Authorization Bearer token。4.2 现象网站房态看板每 10 秒卡顿一次CPU 占用飙升原因v-for渲染 200 房间卡片每次 SSE 推送都触发全量v-for重渲染即使只改一个房间状态。解决改用v-memokey精确控制更新范围。RoomCard :keyroom.id :roomroom /并在RoomCard组件内用defineProps([room])接收确保 props 不变则组件不更新。4.3 现象后台修改房价后小程序订单页仍显示旧价格原因订单创建时价格未固化到订单记录中而是实时查询房型表。当房价变更历史订单价格也跟着变违反「订单不可变」原则。解决订单表order必须包含room_price、service_fee等金额字段创建时从房型表读取并写死。后台提供「价格追溯」功能订单详情页显示「下单时房价¥380」而非「当前房价¥420」。4.4 现象微信模板消息发送失败错误码43101原因微信要求模板消息必须由用户主动触发如点击按钮后 7 天内发送且需用户已授权scope.subscribe.miniprogram。后台直接调用sendTemplateMessage会因无 openid 或过期被拒。解决订单创建后不立即发消息而是存入pending_notifications表字段含openid,template_id,data,expire_at。小程序在onShow时调用wx.login()获取最新 code后台用 code 换取 openid 并发送确保时效性。4.5 现象多前台同时操作同一房间出现“已入住”却还能办理入住原因房态更新未加数据库行锁两个请求同时读取statusvacant都执行UPDATE room SET statusoccupied后者覆盖前者。解决在 Django Model 的check_in方法中使用select_for_update()from django.db import transaction def check_in(self): with transaction.atomic(): room Room.objects.select_for_update().get(idself.room_id) if room.status ! vacant: raise ValidationError(房间非空闲状态) room.status occupied room.save()确保同一房间的并发操作串行化。5. 订单与订餐模块的协同设计如何让餐饮订单不拖垮房态系统5.1 订单生命周期必须拆解为「状态机」而非简单 CRUD酒店订单入住订单与餐饮订单本质不同入住订单有强时序预订→入住→退房→离店餐饮订单有强库存约束某菜品剩余 2 份第 3 单应失败。若共用一个Order模型字段会爆炸check_in_time,check_out_time,delivery_time,is_takeaway...。我们采用「领域模型分离 事件桥接」AccommodationOrder模型专注房态流转字段精简为room_id,guest_name,check_in_time,check_out_time,statuspending,checked_in,checked_out,cancelled。FoodOrder模型专注餐饮履约字段为room_id,items,statusreceived,cooking,ready,delivered,cancelled。桥接事件当AccommodationOrder.status变为checked_in发布事件accommodation_checked_in餐饮服务监听此事件自动为该房间创建「欢迎水果盘」订单当FoodOrder.status变为delivered发布food_delivered前台看板显示「房间 1001 餐饮已送达」。这样房态系统不关心菜品库存餐饮系统不耦合入住流程但通过事件实现业务联动。5.2 订餐库存扣减必须「预占 确认」两阶段防超卖微信小程序下单是瞬时操作但厨房出餐有延迟。若下单即扣减库存用户取消订单时需回滚逻辑复杂易错。我们采用「预占Pre-occupy 确认Confirm」用户提交订餐后台创建FoodOrder状态为pre_occupy同时对每个菜品执行UPDATE food_item SET stock_reserved stock_reserved 1 WHERE id ? AND stock_total - stock_reserved 1;若ROW_COUNT() 0说明库存不足返回失败。厨房确认制作后将订单状态改为cooking执行最终扣减UPDATE food_item SET stock_total stock_total - 1, stock_reserved stock_reserved - 1 WHERE id ?;表格库存字段含义说明字段类型说明stock_totalINT总库存数对外展示stock_reservedINT已预占但未确认的库存数用于并发控制stock_available计算字段stock_total - stock_reserved下单时校验依据5.3 订单管理后台用「时间轴视图」替代传统列表还原真实操作流运营人员最常问“客人说 10:15 下的餐为什么 10:22 才显示已接单” 传统订单列表只显示created_at和updated_at无法定位瓶颈。我们在后台订单详情页实现「操作时间轴」# models/order.py class OrderTimeline(models.Model): order models.ForeignKey(AccommodationOrder, on_deletemodels.CASCADE) action models.CharField(max_length32) # created, checked_in, payment_received operator models.CharField(max_length64) # front_desk_001, system_auto timestamp models.DateTimeField(auto_now_addTrue) notes models.TextField(blankTrue)后台页面用Timeline组件渲染每条记录标注操作人、时间、耗时与上一步对比。例如2024-06-15 10:15:22【客人下单】 by 小程序2024-06-15 10:15:25【系统预占房态】 by system_auto 3s2024-06-15 10:16:01【前台确认入住】 by front_desk_001 36s2024-06-15 10:16:45【支付到账】 by wechat_pay 44s这比看日志高效十倍——问题不在系统慢而在前台确认环节卡了半分钟。6. 真正的“开箱即用”三个我坚持做的交付检查清单很多人以为“开箱即用”就是git clone npm start能跑起来。但在我经手的 12 个酒店系统交付中90% 的返工源于「环境适配盲区」。以下是我每次交付前必做的三件事它们不写在 README 里但决定了客户第一周是否想删库跑路。6.1 检查「房态同步延迟」用真实设备测不用 localhost我从不用curl http://localhost:8000/api/v1/rooms/1001测房态。而是在后台强制将房间 1001 设为occupied拿一部安卓手机打开网站另一部 iPhone 打开小程序用秒表计时从后台点击“入住”按钮开始到两台设备房卡颜色变为绿色的时间合格线≤ 1.5 秒4G 网络下。若超时立刻查nginx是否开启proxy_buffering off以及 WebSocket 是否被 CDN 缓存。血泪经验某次超时 4 秒排查发现腾讯云 CDN 默认缓存 WebSocket Upgrade 请求关掉Websocket Proxy开关后立竿见影。别信文档说的“默认支持”一定要真机测。6.2 检查「微信模板消息到达率」不看后台日志看客人手机后台send_template_message返回errcode:0不代表客人收到。我要求客户现场提供一个测试手机号我们用该号码下一笔测试订单后台触发模板消息客户盯着微信「服务通知」栏记录从触发到弹窗的时间同时抓包https://api.weixin.qq.com/cgi-bin/message/template/send的 response确认msgid字段存在且非 0。若 3 分钟未收到优先检查小程序后台是否开启「订阅消息」权限、模板 ID 是否在「已通过」状态、page参数是否指向有效路径如pages/order/detail?id123而非pages/order/detail。6.3 检查「订餐超卖防护」用 JMeter 模拟 50 并发抢最后一份库存为 1 的菜品是检验系统健壮性的试金石。我用 JMeter 配置线程组50 个线程Ramp-up 时间 1 秒HTTP 请求POST/api/v1/food-ordersbody 含items: [{food_id: F-001, count: 1}]查看结果树统计status_code为 201成功的数量。合格标准成功数 ≤ 1。若出现 2 个以上 201说明预占逻辑失效必须回查food_item.stock_reserved更新语句是否用了SELECT ... FOR UPDATE或数据库事务隔离级别是否为READ COMMITTED。最后说一句实在话所谓“开箱即用”不是让你跳过思考而是把别人踩过的坑提前垫在你脚下。这套系统我亲手陪 7 家酒店上线最短 3 天完成培训部署最长的一次卡在客户自己的路由器禁用了 WebSocket。所以现在我交付时第一件事是帮客户把路由器管理页面翻出来把「应用层网关」里的 WebSocket 选项打上勾——技术可以很酷但让一线员工顺滑用起来才是真正的交付。希望帮到你。本文还有配套的精品资源点击获取