Hotel Reservation — 酒店预订
V1「做个酒店浏览页面,先看看效果」
Section titled “V1「做个酒店浏览页面,先看看效果」”朋友开了一家旅行社,想做个小网站让客户能浏览合作酒店。他说:“你先帮我做个页面出来,能看酒店信息就行,预订功能假的也没关系,我先拿去给客户演示。“你决定先用纯前端做个原型,酒店数据写死在 JSON 里,预订按钮点了只存到 localStorage,不需要后端。这样一天就能出效果。
你要解决什么
Section titled “你要解决什么”做一个酒店浏览和模拟预订页面,数据全部在前端,不依赖任何后端服务。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”做一个酒店浏览和预订页面,要求:
技术栈:单个 HTML 文件 + 内联 CSS/JS(或 React + Vite 均可)数据:页面内嵌 JSON 数组,包含 20 家酒店,每家酒店字段: - id, name, city(北京/上海/杭州/成都/三亚), district - price_per_night(200-2000), rating(3.5-5.0), photos(3张占位图URL) - room_types:[{name: "标准间", capacity: 2, price: X}, {name: "大床房", ...}, {name: "豪华套房", ...}] - amenities:["WiFi", "早餐", "停车场", "泳池", "健身房"] 随机组合
功能:1. 酒店列表页:卡片式展示,每张卡显示照片轮播、名称、城市、价格、评分2. 筛选:按城市下拉筛选,按价格区间滑块筛选(200-2000),按评分筛选(4.0 以上)3. 排序:价格升序/降序、评分降序4. 点击酒店卡进入详情页(同页面内切换),展示所有房型、设施、3张照片5. 选择房型 + 入住日期 + 退房日期,点击"预订"6. 预订信息保存到 localStorage,key 为 "my_bookings"7. "我的预订"页面展示所有已预订记录,支持取消(从 localStorage 删除)
UI 要求:- 响应式布局,手机端卡片单列,桌面端三列- 酒店卡片 hover 有阴影效果- 价格用红色大字体突出显示- 页面加载显示 20 家酒店卡片,照片和信息完整
- 选择城市”上海”后只显示上海的酒店
- 价格滑块筛选和评分筛选联合生效
- 排序切换后列表顺序正确变化
- 点击酒店进入详情,能看到所有房型
- 选择房型和日期后点击预订,“我的预订”中出现记录
- 刷新页面后预订记录还在
- 取消预订后记录消失
你学到了什么
Section titled “你学到了什么”- 前端状态管理:筛选、排序、分页的组合逻辑 → M9(数据处理)
- localStorage 作为轻量持久化,适合原型验证 → M2(数据存储)
- JSON 内嵌数据 vs API 获取数据的权衡 → M1(API 设计)
V2「老板说要真的能订,多个客户同时在用」
Section titled “V2「老板说要真的能订,多个客户同时在用」”演示效果很好,朋友的旅行社决定正式上线。但问题来了:预订是假的,localStorage 只在自己浏览器里有效,客户 A 预订了并不会减少库存,客户 B 完全不知道。朋友说:“至少要能真的预订吧,不同客户看到的库存要是同一份。“你需要加一个后端,管理真实的房间库存,多个用户共享同一份数据。暂时用户量小,SQLite 够用。
你要解决什么
Section titled “你要解决什么”搭建后端实现真实的酒店预订流程,包括库存管理、用户认证和订单记录。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V1 基础上增加真实的预订后端,要求:
后端:Go + Gin + SQLite(用 gorm)
数据模型:- Hotel:id, name, city, district, rating, description- RoomType:id, hotel_id, name, capacity, price_per_night, total_rooms- Inventory:id, room_type_id, date, available_count- User:id, email, password_hash, name, phone- Order:id, user_id, hotel_id, room_type_id, check_in, check_out, total_price, status(pending/confirmed/cancelled), created_at
API 设计:1. POST /api/auth/register — 用户注册2. POST /api/auth/login — 登录返回 JWT3. GET /api/hotels — 酒店列表,支持 ?city=&min_price=&max_price=&rating= 筛选4. GET /api/hotels/:id — 酒店详情含房型列表5. GET /api/hotels/:id/availability?check_in=2024-10-01&check_out=2024-10-03 — 查询指定日期的房间可用数量6. POST /api/orders — 创建预订(需登录),请求体包含 room_type_id, check_in, check_out - 检查日期范围内每天的 available_count >= 1 - 若可用,将日期范围内每天的 available_count 减 1 - 创建 Order 记录,status 为 confirmed - 若不可用,返回 409 Conflict7. GET /api/orders — 我的订单列表8. PUT /api/orders/:id/cancel — 取消订单,恢复库存
初始数据(seed):- 10 家酒店,每家 3 种房型,每种 5 间- 未来 30 天的 Inventory 记录
前端更新:- 注册/登录页面- 酒店详情页显示实时库存数量- 预订时如果库存不足显示"已满房"- 我的订单页从 API 获取- 注册新用户并登录成功,拿到 JWT
- 酒店列表 API 支持筛选参数,返回正确结果
- 查询可用房间返回正确的库存数量
- 预订成功后,再次查询同日期库存减少 1
- 库存为 0 时预订返回 409
- 取消订单后库存恢复
- 用两个浏览器窗口模拟两个用户,一个订完另一个看到库存变化
你学到了什么
Section titled “你学到了什么”- 从前端假数据过渡到真正的关系型数据模型设计 → M2(数据存储)
- RESTful API 设计:资源嵌套、状态查询、操作幂等性 → M1(API 设计)
- JWT 认证流程:注册→登录→携带 token→鉴权 → M1(API 设计)
- 库存是共享可变状态,多用户场景下必须由后端统一管理 → M6(事务与一致性)
V3「十一黄金周,同一间房被两个人订了」
Section titled “V3「十一黄金周,同一间房被两个人订了」”十一黄金周到了,旅行社生意爆好,后台突然收到投诉:客户张先生和李女士都收到了同一间”三亚海景大床房”10月1日的确认短信。你查了数据库,发现同一个房型同一天的两笔订单都是 confirmed 状态。问题出在哪?两个请求几乎同时到达,各自读到 available_count=1,各自减 1,都成功了。SQLite 的并发写入能力有限,而且你的代码没有用事务锁来保护库存扣减。黄金周这种高峰期,必须解决超卖问题。
你要解决什么
Section titled “你要解决什么”用 PostgreSQL + 事务锁彻底解决并发预订导致的超卖问题,并优化高峰期的查询性能。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V2 基础上解决并发超卖问题并提升高峰性能,要求:
后端:Go + Gin + PostgreSQL + Redis
核心改造 — 防超卖:1. 将 SQLite 替换为 PostgreSQL2. 预订 API 改为事务操作: BEGIN SELECT available_count FROM inventory WHERE room_type_id = ? AND date BETWEEN ? AND ? FOR UPDATE -- 行级悲观锁,阻塞其他并发预订 IF any available_count < 1 THEN ROLLBACK, return 409 UPDATE inventory SET available_count = available_count - 1 WHERE room_type_id = ? AND date BETWEEN ? AND ? INSERT INTO orders (...) COMMIT3. 添加数据库约束:ALTER TABLE inventory ADD CHECK (available_count >= 0)4. 记录锁等待指标:每次 FOR UPDATE 等待超过 100ms 记录日志
性能优化:1. Redis 缓存热门酒店数据: - 酒店详情缓存 key: hotel:{id},TTL 10 分钟 - 城市酒店列表缓存 key: hotels:city:{city},TTL 5 分钟 - 预订成功后主动失效相关缓存2. 搜索优化: - PostgreSQL 添加索引:inventory(room_type_id, date)、orders(user_id, status) - 热门城市的搜索结果预热到 Redis
多步预订流程(Saga 模式概念):1. POST /api/orders 改为两阶段: - 第一步:创建 pending 订单 + 锁定库存(15 分钟有效) - 第二步:POST /api/orders/:id/confirm 确认并完成支付(模拟) - 超时未确认:定时任务释放库存,订单标记为 expired2. 定时任务:每分钟扫描 pending 超过 15 分钟的订单
监控 API:- GET /api/admin/stats — 返回:今日订单数、超卖拦截次数、缓存命中率、平均锁等待时间
前端更新:- 预订流程分为"锁定→确认"两步,中间有 15 分钟倒计时- 当库存 <= 3 时显示"仅剩 X 间"提醒- 热门酒店搜索结果加载明显更快- 用 wrk 或脚本并发发送 20 个预订请求抢同一间房,只有 1 个成功,其余返回 409
- 数据库中 available_count 永远 >= 0,没有超卖
- 创建 pending 订单后 15 分钟不确认,库存自动释放
- Redis 缓存命中:第二次查询同一酒店明显更快
- /api/admin/stats 返回正确的统计数据
- 数据库约束生效:手动尝试 UPDATE available_count = -1 被拒绝
你学到了什么
Section titled “你学到了什么”- SELECT FOR UPDATE 悲观锁:最直接的并发控制手段 → M6(事务与一致性)
- 数据库约束作为最后一道防线,应用层 + DB 层双重保护 → M2(数据存储)
- Saga 模式思想:长事务拆成多步,每步可补偿 → M6(事务与一致性)
- Redis 缓存热数据 + 主动失效策略 → M4(缓存策略)
- 从 SQLite 到 PostgreSQL:并发能力是数据库选型的关键考量 → M8(可扩展性)
- 定时任务处理超时订单,保证系统自愈 → M9(数据处理)
V4「搜索结果慢,用户搜完等3秒才出来」
Section titled “V4「搜索结果慢,用户搜完等3秒才出来」”旅行社业务增长到千家合作酒店,注册用户过千。用户反馈最多的问题是搜索慢:“我搜上海的酒店,页面转圈转了 3 秒才出来。“你分析了原因:每次搜索都要查 PostgreSQL 做多表 JOIN(酒店 + 房型 + 库存),城市+日期范围的查询没有高效索引,而且很多用户搜的是同样的条件(同城市同日期)。更糟的是,首页的热门推荐也是实时查库,每个用户访问首页都触发一次重查询。
你要解决什么
Section titled “你要解决什么”用 Redis 缓存搜索结果、优化数据库索引和查询,以及异步预计算库存快照,将搜索响应从 3 秒降到 200ms 以内。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V3 基础上优化搜索性能,要求:
后端:Go + Gin + PostgreSQL + Redis1. 搜索结果缓存: - 缓存 key 设计:search:{city}:{check_in}:{check_out}:{sort}:{page} - TTL 3 分钟(库存变化频率不高,短暂不一致可接受) - 预订成功后主动失效该城市的搜索缓存 - GET /api/hotels/search 优先查 Redis,未命中再查 DB 并回填2. 数据库索引优化: - 复合索引:inventory(room_type_id, date, available_count) - 复合索引:hotels(city, rating DESC) - 覆盖索引:避免回表查询 - 用 EXPLAIN ANALYZE 验证查询计划3. 库存快照异步预计算: - 后台 goroutine 每 5 分钟计算各城市未来 30 天的可用酒店摘要 - 存入 Redis Hash:availability_snapshot:{city}:{date} → {hotel_id: min_price, available_rooms} - 搜索列表页直接用快照数据,点击详情页再查精确库存4. 分页优化: - 使用 cursor-based 分页替代 offset 分页 - cursor 基于 (rating, hotel_id) 复合排序 - 每页 20 条,返回 next_cursor5. 热门推荐预计算: - 每小时计算各城市 Top 10 酒店(按预订量排序) - 缓存到 Redis List:popular:{city},TTL 1 小时 - 首页热门推荐直接读缓存,零 DB 查询
前端:1. 搜索结果骨架屏加载,缓存命中时近乎即时展示2. 搜索列表显示"约 X 家可选"(来自快照),详情页显示精确库存3. 首页热门推荐区域秒开4. 翻页使用无限滚动 + cursor 分页- 相同搜索条件第二次请求响应 < 50ms(命中缓存)
- EXPLAIN ANALYZE 显示查询走了复合索引,无全表扫描
- 库存快照数据与实际库存基本一致(5 分钟内延迟可接受)
- 预订成功后搜索缓存被正确失效,下次搜索返回新数据
- cursor 分页正确工作,翻到最后一页返回空 next_cursor
- 首页热门推荐读取速度 < 20ms
- 整体搜索 P95 响应时间 < 200ms
你学到了什么
Section titled “你学到了什么”- 搜索缓存策略:缓存 key 的维度设计与主动失效 → M4(缓存策略)
- 数据库索引优化:复合索引的列顺序、覆盖索引避免回表 → M2(数据存储)
- 异步预计算:用”最终一致性”换取查询性能 → M5(消息与事件)
- Cursor 分页 vs Offset 分页:大数据量下的性能差异 → M1(API 设计)
- 数据新鲜度与性能的权衡:列表用快照、详情用精确值 → M9(数据处理)
V5「节假日流量是平时的10倍,系统顶不住」
Section titled “V5「节假日流量是平时的10倍,系统顶不住」”又一个黄金周来了,这次用户量达到一万。平时系统运行良好,但节假日搜索量暴增到平时 10 倍,系统接连出问题:后端 CPU 跑满导致接口超时,数据库连接池耗尽,预订请求排队等锁时间过长。更麻烦的是,支付回调偶尔丢失导致订单状态不一致,客服电话被打爆。你意识到单机架构已经到了极限,需要水平扩展。
你要解决什么
Section titled “你要解决什么”通过多实例部署、数据库读写分离、队列化订单处理和熔断机制,让系统在 10 倍流量下也能稳定运行。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V4 基础上实现水平扩展和高可用,要求:
多实例部署:1. 后端部署 3 个实例,Nginx 负载均衡 - 健康检查:GET /health 每 5 秒探测 - 会话无状态:JWT 不依赖服务器内存,所有状态存 Redis/PostgreSQL - 优雅停机:收到 SIGTERM 后等待当前请求完成(最多 30 秒)再退出2. Nginx 配置: - 加权轮询策略 - 慢节点自动摘除(3 次超时后移除,30 秒后重试) - 限流:单 IP 每秒最多 20 个请求
数据库读写分离:1. PostgreSQL 主从配置: - 主库:处理写操作(预订、取消、库存更新) - 从库(1 个副本):处理读操作(搜索、列表、详情查询)2. Go 代码中区分读写数据源: - 写操作用 db.Master() - 读操作用 db.Replica() - 预订后的立即查询走主库(避免主从延迟导致看不到刚下的订单)
队列化订单处理:1. 预订请求解耦: - POST /api/orders 只做参数验证,然后发消息到 Redis Stream(order_queue) - 返回 202 Accepted + order_id,前端轮询订单状态 - 后台 Worker(独立 goroutine 池)消费队列,执行库存锁定和订单创建2. Worker 处理逻辑: - 从 Redis Stream 消费消息(XREADGROUP) - 执行事务锁定库存 + 创建订单 - 更新订单状态到 Redis:order:{id}:status → confirmed/failed - 失败重试 3 次,最终失败标记为 failed 并通知用户3. 订单状态查询: - GET /api/orders/:id/status — 先查 Redis,再查 PostgreSQL
熔断器模式:1. 对下游服务(支付回调、短信通知等)添加熔断器 - 连续 5 次失败 → 熔断器打开(直接返回错误,不调下游) - 30 秒后 → 半开状态(放 1 个请求试探) - 试探成功 → 关闭熔断器,恢复正常2. GET /api/admin/circuit-breakers — 查看各熔断器状态
监控增强:1. 各实例 QPS/延迟/错误率独立上报2. Redis Stream 队列积压长度监控3. 数据库主从延迟监控4. 节假日流量对比大盘(今日 vs 平日平均)
前端:1. 预订提交后显示"排队中...",轮询订单状态直到 confirmed/failed2. 搜索接口超时后自动重试 1 次3. 非核心功能降级:流量过大时隐藏"推荐酒店"模块,减少接口调用- 3 个后端实例正常运行,Nginx 负载均衡分发请求
- 停掉 1 个实例,请求自动切到其他实例,用户无感知
- 搜索请求走从库,预订请求走主库,日志中可验证
- 预订后立即查询订单走主库,能看到刚创建的订单
- 高并发预订时请求进入队列,Worker 有序处理,无超卖
- Redis Stream 中消费进度正常推进,无大量积压
- 模拟下游服务故障,熔断器正确打开/半开/关闭
- 单 IP 限流生效:超过 20 请求/秒返回 429
- 压测 10 倍流量,系统整体可用,无雪崩
你学到了什么
Section titled “你学到了什么”- 水平扩展基础:无状态应用 + 负载均衡 + 共享存储 → M8(可扩展性)
- 读写分离:用从库分担读压力,主从延迟的应对策略 → M3(数据复制)
- 消息队列解耦:削峰填谷,异步处理高并发写入 → M5(消息与事件)
- 熔断器模式:防止级联故障扩散到整个系统 → M16(DevOps)
- 优雅降级:非核心功能在高峰期主动关闭 → M8(可扩展性)
- Nginx 限流与健康检查:入口层的流量管控 → M13(网络与内容分发)
V6「扩张到全国100个城市,数据库单表千万行」
Section titled “V6「扩张到全国100个城市,数据库单表千万行」”旅行社融资成功,业务扩张到全国 100 个城市,合作酒店上万家,日活用户突破十万。数据库问题全面爆发:inventory 表已有千万行,搜索即使有索引也开始变慢;单个 PostgreSQL 主库写入 QPS 到达瓶颈,高峰期锁等待时间飙升;热门城市(三亚、丽江)假期间库存竞争极其激烈。与此同时,产品经理提出了动态定价需求——根据供需关系实时调整价格,这进一步增加了系统复杂度。
你要解决什么
Section titled “你要解决什么”通过数据库分片、独立搜索服务、Saga 跨分片事务、动态定价引擎和多级缓存,支撑全国级酒店预订平台。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V5 基础上构建全国级酒店预订平台,要求:
数据库按城市分片:1. 分片策略:按城市维度分库 - shard_1:华东城市(上海、杭州、南京...) - shard_2:华南城市(广州、深圳、三亚...) - shard_3:华北城市(北京、天津、青岛...) - shard_4:西部城市(成都、重庆、昆明...)2. 分片路由中间件: - 根据 city 参数路由到对应分片 - 跨城市搜索(如"搜索全国五星酒店")并行查询所有分片,合并结果3. 每个分片独立的主从配置(1 主 1 从)4. 全局表(不分片):用户表、订单总表(只存 order_id + shard_id 的映射)
独立搜索服务(Elasticsearch):1. 酒店数据同步到 Elasticsearch: - 字段:name, city, district, rating, price_range, amenities, room_types, available_dates - 酒店信息变更时通过消息队列异步同步2. 搜索 API 改为查 Elasticsearch: - 支持全文搜索(酒店名、地区) - 支持多维度筛选 + 聚合(各价位段酒店数量) - 搜索响应 < 100ms3. 预订仍走 PostgreSQL 分片(强一致性)
Saga 模式跨分片预订:1. 场景:用户一次预订多个城市的酒店(行程规划)2. Saga 协调器: - Step 1: 在 shard_1 锁定上海酒店库存 - Step 2: 在 shard_2 锁定三亚酒店库存 - Step 3: 创建统一订单记录 - 任一步骤失败 → 执行补偿操作(释放已锁定的库存)3. Saga 状态机持久化到 Redis,支持断点恢复
动态定价引擎:1. 定价因素: - 供需比:available_rooms / total_rooms,低于 30% 加价 - 时间因素:节假日系数(1.5-3.0 倍)、提前天数折扣 - 竞对参考:同城市同星级酒店的平均价2. 价格计算流程: - 基础价 × 供需系数 × 节假日系数 × 提前预订折扣 = 最终价 - 最终价有上下限保护(0.5x - 5x 基础价)3. 价格缓存: - Redis 缓存当前价格,TTL 10 分钟 - 库存变化时触发价格重算4. GET /api/hotels/:id/pricing?check_in=&check_out= — 返回每日价格明细
多级缓存架构:1. 本地缓存(Go sync.Map):热门酒店基础信息,TTL 1 分钟2. Redis 缓存:搜索结果、库存快照、价格,TTL 3-10 分钟3. PostgreSQL:最终一致的持久化数据4. 缓存更新策略: - 写穿透:预订成功后更新 Redis + 失效本地缓存 - 定时刷新:价格和库存快照定时从 DB 重建 - 缓存击穿防护:热 key 用 singleflight 防止并发回源
监控与运维:1. 各分片负载监控:QPS、连接数、慢查询2. Elasticsearch 索引健康度和同步延迟3. Saga 事务成功率和补偿触发率4. 动态定价效果大盘:各城市平均售价趋势、收益对比
前端:1. 搜索体验升级:输入即搜索(debounce 300ms),关键词高亮2. 价格日历:显示未来 30 天每日价格波动,标注"今日特价"3. 行程规划:支持多城市酒店一键预订4. 价格走势图:展示该酒店过去 30 天价格变化- 不同城市的酒店数据存储在对应分片,查询路由正确
- 跨城市搜索(全国五星酒店)返回所有分片的结果,响应 < 500ms
- Elasticsearch 搜索支持全文匹配和多维度筛选
- 酒店信息更新后 Elasticsearch 在 10 秒内同步
- 多城市预订 Saga 正常完成,任一步骤失败正确触发补偿
- Saga 中途服务重启,能从 Redis 恢复状态继续执行
- 动态定价在库存紧张时正确加价,价格在合理范围内
- 多级缓存逐层命中:本地 → Redis → DB,日志可追踪
- singleflight 防止热 key 并发回源,相同 key 只有 1 个请求查 DB
- 各分片负载均衡,无单分片过热
你学到了什么
Section titled “你学到了什么”- 数据库分片策略:按业务维度分片降低单库压力 → M8(可扩展性)
- 搜索与交易分离:Elasticsearch 处理搜索、PostgreSQL 处理事务 → M2(数据存储)
- Saga 模式实战:跨分片的分布式事务与补偿机制 → M6(事务与一致性)
- 动态定价:供需模型在工程中的实现 → M9(数据处理)
- 多级缓存架构:本地 → 分布式 → 持久化的三层缓存体系 → M4(缓存策略)
- singleflight 防缓存击穿:Go 并发控制的经典应用 → M4(缓存策略)
- 消息队列实现数据异步同步:保证搜索索引最终一致 → M5(消息与事件)
- 全国级系统的可观测性:分片级监控 + 业务指标大盘 → M15(可观测性)