优先级: P1(业务增长模块)
开发周期: 2 周
实时管理各供应商的房态和房量,支持库存预占、同步和释放,避免超售。
| 功能 | 描述 |
|---|---|
| 实时库存同步 | 定时/实时从供应商拉取库存,或接收供应商推送 |
| 库存预占 | 下单时预占库存(带 TTL,超时自动释放) |
| 库存释放 | 取消订单时释放预占库存 |
| 关停房管理 | 管理酒店的关停房日期(Stop Sell) |
| 房量分配 | 按渠道/代理商分配房量配额 |
| 库存告警 | 库存低于阈值时告警 |
| 超售保护 | 本地库存计数 + 供应商实时校验双重保护 |
库存管理模块主要适用于 direct_procure 供应商,但直采供应商也可能"有价即有房"(available_rooms=NULL),不一定要管理库存数量。不同连接模式的库存处理差异:
注意:
available_rooms允许 NULL。NULL 表示有价即有房、不限量;有值表示真实库存数、售完为止。
| 维度 | direct_connect(直连) | direct_procure(直采) |
|---|---|---|
| 库存管理 | ❌ 不涉及 | ✅ 完整生命周期管理 |
| 库存概念 | 有价即有房,无库存数量 | 可能有明确房量数字(available_rooms 有值),也可能有价即有房(available_rooms=NULL) |
| 数据写入 | 不写入库存表 | inventory_snapshots 定期同步 |
| 下单操作 | 无需预占/扣减库存 | Redis DECRBY 预占 → 确认转占用 |
| 库存同步 | 不适用 | 定时拉取(pull)/ 供应商推送(push) |
| 关停房管理 | 不适用 | stop_sells 表管理 |
| 房量分配 | 不适用 | inventory_allocations 按渠道/代理商分配 |
设计原则:库存模块在查询供应商数据时,应先检查
connection_mode字段。对于direct_connect供应商,跳过所有库存相关逻辑。对于direct_procure供应商,还需检查available_rooms是否为 NULL(NULL 表示有价即有房,无需校验库存数量)。
下单时:
1. Redis DECRBY 预占库存 (原子操作)
2. 若预占成功 → 创建订单
3. 若预占失败 → 返回库存不足
供应商确认后:
1. 将预占库存转为确认占用
2. Redis 更新实际可用库存
供应商拒绝/取消/超时:
1. Redis INCRBY 释放预占库存
2. 通知代理商
定时任务(每5分钟):
1. 清理超时未确认的预占库存
2. 同步供应商最新库存
-- 实时库存表(热数据,频繁更新)
CREATE TABLE inventory_snapshots (
id BIGSERIAL PRIMARY KEY,
supplier_code VARCHAR(32) NOT NULL,
supplier_hotel_id VARCHAR(64) NOT NULL,
room_type_code VARCHAR(32) NOT NULL,
stay_date DATE NOT NULL, # 入住日期
total_rooms INT DEFAULT 0, # 总房量
sold_rooms INT DEFAULT 0, # 已售
blocked_rooms INT DEFAULT 0, # 关停房
available_rooms INT DEFAULT 0, # 可用房量
last_synced_at TIMESTAMPTZ,
sync_source VARCHAR(32) DEFAULT 'pull', # pull/push/manual
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(supplier_code, supplier_hotel_id, room_type_code, stay_date)
);
CREATE INDEX idx_inventory_date ON inventory_snapshots (supplier_code, stay_date);
-- 库存预占记录表
CREATE TABLE inventory_holds (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL REFERENCES orders(id),
supplier_code VARCHAR(32) NOT NULL,
supplier_hotel_id VARCHAR(64) NOT NULL,
room_type_code VARCHAR(32) NOT NULL,
stay_date DATE NOT NULL,
room_count SMALLINT NOT NULL DEFAULT 1,
status VARCHAR(20) DEFAULT 'active', -- active/confirmed/released/expired
expires_at TIMESTAMPTZ NOT NULL, # 预占过期时间
released_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_inventory_holds_order ON inventory_holds (order_id);
CREATE INDEX idx_inventory_holds_status ON inventory_holds (status, expires_at);
CREATE INDEX idx_inventory_holds_expire ON inventory_holds (status, expires_at)
WHERE status = 'active';
-- 关停房日期表
CREATE TABLE stop_sells (
id BIGSERIAL PRIMARY KEY,
supplier_code VARCHAR(32) NOT NULL,
supplier_hotel_id VARCHAR(64) NOT NULL,
room_type_code VARCHAR(32), # 空=整个酒店
start_date DATE NOT NULL,
end_date DATE NOT NULL,
reason VARCHAR(128),
source VARCHAR(32) DEFAULT 'supplier', -- supplier/manual
created_at TIMESTAMPTZ DEFAULT NOW(),
created_by VARCHAR(64)
);
-- 房量配额分配表
CREATE TABLE inventory_allocations (
id BIGSERIAL PRIMARY KEY,
supplier_code VARCHAR(32) NOT NULL,
supplier_hotel_id VARCHAR(64) NOT NULL,
room_type_code VARCHAR(32) NOT NULL,
stay_date DATE NOT NULL,
agent_code VARCHAR(32), # 分配给特定代理商
channel_code VARCHAR(32), # 或分配给渠道
allocated_rooms INT NOT NULL DEFAULT 0, # 分配房量
used_rooms INT NOT NULL DEFAULT 0, # 已用房量
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(supplier_code, supplier_hotel_id, room_type_code, stay_date,
COALESCE(agent_code, ''), COALESCE(channel_code, ''))
);
-- Redis 库存缓存 Key 设计
# inventory:{supplier_code}:{hotel_id}:{room_code}:{date} → INT (可用房量)
# hold:{order_id} → JSON (预占信息, TTL = 30min)
# 库存管理 API (内部)
POST /api/v1/inventory/hold # 预占库存
# 请求: { "order_id": 123, "supplier_code": "HB", "hotel_id": "X", "room_code": "DBL", "date": "2025-03-01", "count": 1 }
DELETE /api/v1/inventory/hold/:order_id # 释放库存
GET /api/v1/inventory/status # 查询库存状态
POST /api/v1/inventory/sync/:supplier_code # 触发库存同步
# 关停房 API (管理后台)
GET /api/v1/stop-sells # 关停房列表
POST /api/v1/stop-sells # 创建关停房
DELETE /api/v1/stop-sells/:id # 删除关停房
# 配额管理 API
GET /api/v1/inventory/allocations # 配额列表
POST /api/v1/inventory/allocations # 创建配额
PUT /api/v1/inventory/allocations/:id # 更新配额
功能描述
供应商可设置酒店的可售日期范围(sell_from, sell_to),不同渠道/代理商可以有不同的可售日期范围。超出范围的查询直接返回无房,不穿透供应商,减少无效请求。
数据模型
-- 可售日期范围表
CREATE TABLE sell_date_range (
id BIGSERIAL PRIMARY KEY,
supplier_id BIGINT NOT NULL REFERENCES suppliers(id),
hotel_id BIGINT NOT NULL,
room_type_id BIGINT, -- NULL = 该酒店所有房型
channel_id BIGINT REFERENCES channels(id), -- NULL = 全局默认
agent_id BIGINT REFERENCES agents(id), -- NULL = 不限代理商(配合 channel_id 使用)
sell_from DATE NOT NULL, -- 可售开始日期
sell_to DATE NOT NULL, -- 可售结束日期
overwrite_global BOOLEAN DEFAULT FALSE, -- true = 覆盖全局设置
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_sdr_supplier_hotel ON sell_date_range (supplier_id, hotel_id, room_type_id);
CREATE INDEX idx_sdr_channel ON sell_date_range (channel_id, sell_from, sell_to);
API 接口
GET /api/admin/sell-date-ranges # 可售日期范围列表
POST /api/admin/sell-date-ranges # 创建可售日期范围
PUT /api/admin/sell-date-ranges/:id # 更新
DELETE /api/admin/sell-date-ranges/:id # 删除
GET /api/internal/sell-date-range/:supplier_id/:hotel_id # 内部:查询可售日期范围
业务逻辑
overwrite_global = true 的规则覆盖全局默认设置sdr:{supplier_id}:{hotel_id}:{channel_id}),TTL 10min功能描述
控制预订的时间窗口:最少提前几天预订(min_advance_days),最远可以订到哪天(max_advance_days)。不同渠道/代理商可配置不同窗口。超出窗口的查询直接返回无房。
数据模型
-- 预定窗口表
CREATE TABLE booking_window (
id BIGSERIAL PRIMARY KEY,
supplier_id BIGINT NOT NULL REFERENCES suppliers(id),
hotel_id BIGINT NOT NULL,
room_type_id BIGINT, -- NULL = 该酒店所有房型
channel_id BIGINT REFERENCES channels(id), -- NULL = 全局默认
agent_id BIGINT REFERENCES agents(id), -- NULL = 不限代理商
min_advance SMALLINT NOT NULL DEFAULT 1, -- 最少提前天数(如 1 = 当天不能订)
max_advance SMALLINT NOT NULL DEFAULT 365, -- 最远可订天数
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_bw_supplier_hotel ON booking_window (supplier_id, hotel_id, room_type_id);
CREATE INDEX idx_bw_channel ON booking_window (channel_id);
API 接口
GET /api/admin/booking-windows # 预定窗口列表
POST /api/admin/booking-windows # 创建预定窗口
PUT /api/admin/booking-windows/:id # 更新
DELETE /api/admin/booking-windows/:id # 删除
GET /api/internal/booking-window/:supplier_id/:hotel_id # 内部:查询预定窗口
业务逻辑
check_in >= TODAY + min_advance 且 check_in <= TODAY + max_advancebw:{supplier_id}:{hotel_id}:{channel_id}),TTL 10min功能描述
控制最少/最多入住天数(min_los / max_los),并支持连住折扣(住 N 晚以上给折扣)。入住天数不在范围内直接返回无房。
数据模型
-- 连住规则表
CREATE TABLE los_rule (
id BIGSERIAL PRIMARY KEY,
supplier_id BIGINT NOT NULL REFERENCES suppliers(id),
hotel_id BIGINT NOT NULL,
room_type_id BIGINT, -- NULL = 该酒店所有房型
channel_id BIGINT REFERENCES channels(id), -- NULL = 全局默认
agent_id BIGINT REFERENCES agents(id), -- NULL = 不限代理商
min_los SMALLINT NOT NULL DEFAULT 1, -- 最少入住天数
max_los SMALLINT, -- NULL = 不限最多天数
discount_type VARCHAR(16), -- percentage/fixed/none
discount_value DECIMAL(8, 2) DEFAULT 0, -- 折扣值(百分比或固定金额)
effective_from DATE NOT NULL DEFAULT CURRENT_DATE,
effective_to DATE, -- NULL = 长期有效
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_los_supplier_hotel ON los_rule (supplier_id, hotel_id, room_type_id);
CREATE INDEX idx_los_channel ON los_rule (channel_id, effective_from, effective_to);
API 接口
GET /api/admin/los-rules # 连住规则列表
POST /api/admin/los-rules # 创建连住规则
PUT /api/admin/los-rules/:id # 更新
DELETE /api/admin/los-rules/:id # 删除
GET /api/internal/los-rule/:supplier_id/:hotel_id # 内部:查询连住规则
业务逻辑
nights = check_out - check_innights < min_los 或 max_los 不为空且 nights > max_los,直接返回无房nights >= min_los 且设置了折扣,查价引擎在价格计算阶段应用连住折扣percentage(如 9 折 = 10% off)或 fixed(每晚减固定金额)功能描述
供应商可将某日期的某房型库存分配给不同渠道,各渠道独立计数。某渠道售完后自动 stop_sell。支持共用 overbooking 池。
数据模型
-- 房量分配表
CREATE TABLE allocation (
id BIGSERIAL PRIMARY KEY,
supplier_id BIGINT NOT NULL REFERENCES suppliers(id),
hotel_id BIGINT NOT NULL,
room_type_id BIGINT NOT NULL,
date DATE NOT NULL,
channel_id BIGINT, -- NULL = 共用池(overbooking)
agent_id BIGINT REFERENCES agents(id), -- NULL = 不限代理商(配合 channel_id)
allocated_qty INT NOT NULL DEFAULT 0, -- 分配房量
sold_qty INT NOT NULL DEFAULT 0, -- 已售房量
released_at TIMESTAMPTZ, -- 释放时间(售罄时间)
status VARCHAR(16) DEFAULT 'active', -- active/exhausted/released
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(supplier_id, hotel_id, room_type_id, date, COALESCE(channel_id, 0))
);
CREATE INDEX idx_alloc_date ON allocation (supplier_id, hotel_id, room_type_id, date);
CREATE INDEX idx_alloc_channel ON allocation (channel_id, date, status);
API 接口
GET /api/admin/allocations # 房量分配列表
POST /api/admin/allocations # 创建分配(支持批量)
PUT /api/admin/allocations/:id # 更新分配数量
DELETE /api/admin/allocations/:id # 删除分配
POST /api/admin/allocations/batch # 批量设置分配(日期范围)
GET /api/internal/allocation/:supplier_id/:hotel_id/:date # 内部:查询某日分配情况
业务逻辑
sold_qty >= allocated_qty),自动标记为 exhausted,返回无房channel_id = NULL)在所有渠道分配用完后兜底使用sold_qty 回退,exhausted 状态自动恢复为 activealloc:{supplier_id}:{hotel_id}:{room_type_id}:{date}:{channel_id})功能描述
供应商可以关闭某日期某房型(维护、满房、活动等原因),支持批量关停(日期范围)。关停后所有渠道立即生效,查价时直接返回无房。
注:此功能与 6.8 原有的
stop_sells表功能重叠,此处定义增强版的关停管理,支持渠道级别关停和更完善的状态管理。
数据模型
-- 增强版关停房表(替代原 stop_sells 表或作为补充)
CREATE TABLE stop_sell_v2 (
id BIGSERIAL PRIMARY KEY,
supplier_id BIGINT NOT NULL REFERENCES suppliers(id),
hotel_id BIGINT NOT NULL,
room_type_id BIGINT, -- NULL = 整个酒店关停
channel_id BIGINT REFERENCES channels(id), -- NULL = 所有渠道关停
stop_from DATE NOT NULL, -- 关停开始日期
stop_to DATE NOT NULL, -- 关停结束日期
reason VARCHAR(128) NOT NULL, -- maintenance/full/event/seasonal/other
reason_detail TEXT, -- 详细说明
status VARCHAR(16) DEFAULT 'active', -- active/cancelled/expired
created_by VARCHAR(64) NOT NULL, -- 创建人
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_ss_v2_hotel_date ON stop_sell_v2 (supplier_id, hotel_id, room_type_id, stop_from, stop_to);
CREATE INDEX idx_ss_v2_channel ON stop_sell_v2 (channel_id, status, stop_from, stop_to);
CREATE INDEX idx_ss_v2_status ON stop_sell_v2 (status, stop_from, stop_to);
API 接口
GET /api/admin/stop-sells # 关停房列表
POST /api/admin/stop-sells # 创建关停(支持日期范围批量)
PUT /api/admin/stop-sells/:id # 更新关停
DELETE /api/admin/stop-sells/:id # 取消关停
POST /api/admin/stop-sells/batch # 批量关停(多酒店/多房型)
GET /api/internal/stop-sell/:supplier_id/:hotel_id # 内部:查询关停状态
业务逻辑
channel_id = NULL 表示所有渠道关停,指定 channel_id 则仅对该渠道关停ss:{supplier_id}:{hotel_id}:{room_type_id}),TTL 5min,变更时主动刷新| 关联模块 | 关系 |
|---|---|
| 查价引擎 | 提供实时库存数据 |
| 订单管理 | 下单预占、取消释放 |
| 匹配管理 | 供应商编码映射 |
| 管理后台 | 库存监控和关停房管理 |
| 供应商API | 拉取/接收库存变更推送 |