版本: v1.0
适用场景: 一人公司 + AI 搭档,技术驱动,几万块启动资金,Demo 驱动融资
目标读者: 创始人/技术负责人、投资人
编写日期: 2025-01
B2B2B 酒店分销平台连接三方:酒店/批发商(供应商) → 本平台 → OTA/旅行社/代理商(采购方)。
核心价值: - 聚合供应商:统一接入多个酒店批发商和直签酒店 - 标准化数据:异构房型、价格、库存统一映射 - 智能分发:根据渠道需求匹配最优价格和可用性 - 自动化运营:订单、结算、对账全流程自动化
代理商查价 → 平台聚合多供应商报价 → 代理商下单 → 平台向供应商下单
→ 供应商确认/拒绝 → 订单履约 → 对账结算 → 佣金结算
| 指标 | MVP 目标 | 成熟期目标 |
|---|---|---|
| 接入供应商数 | 3-5 家 | 50+ 家 |
| 日均查价 QPS | 100 | 10,000+ |
| 订单转化率 | 5% | 15%+ |
| 查价响应 P99 | < 2s | < 500ms |
| 订单自动化率 | 80% | 98%+ |
flowchart TB
subgraph Clients["客户端层"]
A1["代理商API接入
OTA/旅行社"]
A2["管理后台Web
运营人员"]
A3["供应商对接
API/文件"]
end
subgraph Gateway["网关层"]
G1["API Gateway
Kong/Nginx"]
G2["认证鉴权
JWT + API Key"]
G3["限流熔断
Sentinel"]
G4["日志采集
+ 请求追踪"]
end
subgraph Services["业务服务层"]
S1["酒店基库服务
Hotel Service"]
S2["匹配服务
Mapping Service"]
S3["查价引擎
Pricing Engine"]
S4["订单服务
Order Service"]
S5["销售服务
Sales Service"]
S6["价格服务
Rate Service"]
S7["结算服务
Settlement Service"]
S8["库存服务
Inventory Service"]
S9["风控服务
Risk Control"]
S10["数据智能
Data Intelligence"]
end
note_left[connection_mode决定服务路由: direct_connect实时查价 direct_procure查本地库存]
subgraph Middleware["中间件层"]
MQ["消息队列
RabbitMQ"]
CACHE["缓存层
Redis Cluster"]
CONFIG["配置中心
Nacos"]
end
subgraph Data["数据层"]
DB["PostgreSQL
主库 (业务数据)"]
RO["PostgreSQL
只读副本"]
CH["ClickHouse
分析库"]
ES["Elasticsearch
搜索引擎"]
OBJ["MinIO/OSS
对象存储"]
end
subgraph External["外部对接"]
E1["供应商API
Hotelbeds/Expedia等"]
E2["支付网关
Stripe/支付宝"]
E3["通知服务
Email/SMS"]
E4["汇率API
ExchangeRate"]
end
A1 --> G1
A2 --> G1
A3 --> G1
G1 --> G2 --> G3 --> G4
G1 --> S1
G1 --> S3
G1 --> S4
G1 --> S5
G3 --> S9
S1 <--> S2
S2 --> S3
S3 <--> S6
S3 <--> S8
S4 --> S7
S4 --> S8
S5 --> S7
S6 --> S3
S8 --> S3
S1 <--> MQ
S4 <--> MQ
S8 <--> MQ
S1 --> DB
S2 --> DB
S3 --> CACHE
S3 --> DB
S4 --> DB
S5 --> DB
S6 --> DB
S6 --> CACHE
S7 --> DB
S8 --> DB
S8 --> CACHE
S10 --> CH
S10 --> ES
S1 --> ES
Services --> External
class A1,A2,A3,G1,G2,G3,G4,S1,S2,S3,S4,S5,S6,S7,S8,S9,S10,MQ,CACHE,CONFIG,DB,RO,CH,ES,OBJ,E1,E2,E3,E4 process
classDef process fill:#1a2744,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
classDef decision fill:#3a2a1a,stroke:#f0883e,stroke-width:2px,color:#f0883e
classDef done fill:#1a3a2a,stroke:#3fb950,stroke-width:1px,color:#3fb950
classDef error fill:#3a1a1a,stroke:#f85149,stroke-width:2px,color:#f85149
classDef warning fill:#3a2a1a,stroke:#d29922,stroke-width:1px,color:#d29922
classDef skip fill:#21262d,stroke:#484f58,color:#8b949e
| 层级 | 技术选型 | 理由 |
|---|---|---|
| 后端语言 | Go (Golang) | 高并发性能优异,编译型语言部署简单,标准库丰富,适合一人团队高效开发。goroutine 天然适合大量供应商并发查价场景 |
| Web 框架 | Gin / Fiber | 轻量高性能,中间件生态完善,社区活跃。Fiber 更快但 Gin 生态更成熟 |
| 关系型数据库 | PostgreSQL 15+ | JSONB 支持灵活 schema(酒店数据异构性强),全文搜索、GIS 地理查询、强事务一致性,开源免费 |
| 缓存 | Redis 7 (单节点 → Cluster) | 查价结果缓存、库存缓存、分布式锁、限流计数。内存型响应快,数据结构丰富 |
| 消息队列 | RabbitMQ | MVP 阶段够用,管理界面友好,routing key 灵活。后期可迁移至 Kafka 处理高吞吐 |
| 搜索引擎 | Elasticsearch | 酒店搜索(地理位置、设施、关键词)、日志分析。倒排索引天然适合搜索 |
| 分析数据库 | ClickHouse | 列式存储,BI 查询快 100 倍+。适合聚合统计、趋势分析、报表 |
| API 网关 | Kong (开源版) | 插件丰富(限流、鉴权、日志),性能好,开源免费 |
| 容器化 | Docker + Docker Compose | 一人团队首选,环境一致,部署简单。后期迁移 K8s |
| CI/CD | GitHub Actions | 免费额度足够,与 GitHub 深度集成,配置即代码 |
| 前端 | Vue 3 + Element Plus | 管理后台首选,组件丰富,上手快。或用 React + Ant Design |
| 监控 | Prometheus + Grafana | 开源标配,指标采集 + 可视化。AlertManager 告警 |
| 日志 | Loki (配合 Grafana) | 轻量级日志聚合,与 Grafana 统一界面。或用 ELK |
| 对象存储 | MinIO (自建) / 阿里云 OSS | 存储合同文件、报表、日志归档 |
| 定时任务 | Asynq (Go) | 基于 Redis 的任务队列,比 cron 更可靠,支持重试和延迟 |
| 工具 | 用途 |
|---|---|
| GoLand / VS Code + Go 插件 | 开发 IDE |
| Docker Desktop | 本地开发环境 |
| Postman / Bruno | API 调试 |
| DBeaver | 数据库管理 |
| TablePlus | PostgreSQL 客户端 |
| Draw.io / Excalidraw | 架构图绘制 |
| Swagger / OpenAPI | API 文档 |
| golang-migrate | 数据库版本管理 |
| Mockoon | 供应商 API Mock |
| 组件 | MVP 方案 | 生产方案 |
|---|---|---|
| 服务器 | 1-2 台 4C8G 云服务器 (阿里云/腾讯云) | K8s 集群 + 弹性伸缩 |
| 数据库 | 与应用同机部署,主从分离 | RDS + 只读副本 |
| 缓存 | 同机 Redis 单实例 | Redis Cluster / 阿里云 Redis |
| 存储 | 本地磁盘 + MinIO | OSS / S3 |
| 域名 | 1 个 .com 域名 | 多域名(API/管理/文档) |
| CDN | 不需要 | CloudFlare / 阿里云 CDN |
| SSL | Let's Encrypt 免费证书 | 同 |
flowchart LR
subgraph P0["P0 核心模块"]
API["API网关"]
HS["酒店基库"]
MS["匹配管理"]
PE["查价引擎"]
OM["订单管理"]
AM["管理后台"]
end
subgraph P1["P1 增长模块"]
SM["销售管理"]
PM["价格管理"]
BM["库存管理"]
PAP["进阶定价"]
ST["结算管理"]
RC["风控体系"]
end
subgraph P2["P2 智能模块"]
DI["数据智能"]
end
API --> PE
API --> OM
API --> AM
HS --> MS
MS --> PE
PM --> PE
PAP["进阶定价"] -.->|"Phase3"| PE
BM --> PE
SM -.->|"Phase1"| PE
RC --> PE
PE --> OM
SM --> OM
SM --> ST
OM --> ST
PM --> ST
BM --> OM
OM --> DI
PE --> DI
SM --> DI
ST --> DI
AM --> HS
AM --> MS
AM --> SM
AM --> PM
AM --> OM
AM --> ST
AM --> BM
AM --> RC
AM --> DI
PE -.->|"降级查询"| CACHE["Redis缓存"]
class API,HS,MS,PE,OM,AM,SM,PM,BM,ST,RC,DI,CACHE,PAP process
classDef process fill:#1a2744,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
classDef decision fill:#3a2a1a,stroke:#f0883e,stroke-width:2px,color:#f0883e
classDef done fill:#1a3a2a,stroke:#3fb950,stroke-width:1px,color:#3fb950
classDef error fill:#3a1a1a,stroke:#f85149,stroke-width:2px,color:#f85149
classDef warning fill:#3a2a1a,stroke:#d29922,stroke-width:1px,color:#d29922
classDef skip fill:#21262d,stroke:#484f58,color:#8b949e
查价、试单、下单三条链路均采用三阶段模型,详见各模块设计文档。
flowchart TD
START(["代理商发起查价请求"]) --> P1_BOX
subgraph P1["Phase 1: 查价前 Pre-Query"]
P1_BOX --> GW["Gateway
鉴权+限流"]
GW --> CB["熔断检查
供应商/代理商"]
CB --> VIS["可见性过滤
白名单/黑名单"]
VIS --> CACHE{"L1/L2 缓存
是否有结果?"}
CACHE -->|"缓存命中"| RET_CACHE["直接返回
回填L1"]
CACHE -->|"未命中"| MAP["匹配服务
供应商映射解析"]
MAP --> CH_CFG["渠道配置加载"]
end
P1_BOX -->|"熔断/无权限"| BLOCKED(["阻断返回"])
CH_CFG --> P2_BOX
subgraph P2["Phase 2: 查价 Query"]
P2_BOX --> PARA["并发查价
errgroup + 超时"]
PARA --> DC["直连供应商
调SA实时API"]
PARA --> DP["直采供应商
查本地库存"]
DC --> DC_FAIL{"超时/错误?"}
DC_FAIL -->|"是"| SKIP["跳过该供应商"]
DC_FAIL -->|"否"| DC_OK["原始报价"]
DP --> DP_OK["原始报价"]
end
DC_OK --> P3_BOX
DP_OK --> P3_BOX
SKIP --> P3_BOX
subgraph P3["Phase 3: 查价后 Post-Query"]
P3_BOX --> AGG["结果聚合
去重+排序+分页"]
AGG --> MKUP["Markup 加价"]
MKUP --> DYN["动态定价调整"]
DYN --> PROMO["促销匹配"]
PROMO -> FX["汇率换算"]
FX --> WR_CACHE["写入L2+L1缓存"]
WR_CACHE --> LOG_Q["异步写查价日志"]
LOG_Q --> RETURN(["返回最终报价"])
end
class RET_CACHE,RETURN done
class BLOCKED error
class START,GW,CB,VIS,CACHE,MAP,CH_CFG,PARA,DC,DP,DC_FAIL,SKIP,DC_OK,DP_OK,AGG,MKUP,DYN,PROMO,FX,WR_CACHE,LOG_Q process
classDef process fill:#1a2744,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
classDef decision fill:#3a2a1a,stroke:#f0883e,stroke-width:2px,color:#f0883e
classDef done fill:#1a3a2a,stroke:#3fb950,stroke-width:1px,color:#3fb950
classDef error fill:#3a1a1a,stroke:#f85149,stroke-width:2px,color:#f85149
flowchart TD
SEARCH(["查价结果"]) --> V1
subgraph V1["Verify Phase 1: 试单前"]
V1 --> V_EXP["报价有效期检查"]
V_EXP --> V_RISK["风控预检
信用额度"]
V_RISK --> V_CB["熔断检查"]
end
V_CB --> V2
subgraph V2["Verify Phase 2: 试单"]
V2 --> V_SUP["供应商实时价格确认"]
V_SUP --> V_DEV{"价格偏差
超阈值?"}
end
V2 --> V3
subgraph V3["Verify Phase 3: 试单后"]
V3 --> V_FREEZE["冻结信用额度"]
V_FREEZE --> V_TOKEN["生成 verify_token"]
V_TOKEN --> V_DONE(["返回确认价格
+ verify_token"])
end
V_DONE --> O1
subgraph O1["Order Phase 1: 下单前"]
O1 --> O_TOKEN["verify_token 校验"]
O_TOKEN --> O_RECHECK["报价二次校验"]
O_RECHECK --> O_RISK["风控检查
信用+价格异常"]
O_RISK --> O_INV["库存预检
直采模式"]
end
O1 -->|"阻断"| O_BLOCK(["返回错误"])
O_INV --> O2
subgraph O2["Order Phase 2: 下单"]
O2 --> O_CREATE["创建订单 PG事务"]
O_CREATE --> O_LOCK["库存预占
Redis DECRBY"]
O_LOCK --> O_CREDIT["信用冻结"]
O_CREDIT --> O_SUP["调供应商API下单"]
O_SUP --> O_RESULT{"供应商响应"}
O_RESULT -->|"确认"| PENDING["PENDING_CONFIRM"]
O_RESULT -->|"拒绝"| O_REJECT["REJECTED
释放库存+信用"]
end
PENDING --> O3
subgraph O3["Order Phase 3: 下单后"]
O3 --> O_NOTIFY["通知代理商"]
O3 --> O_TIMEOUT["设置超时任务"]
O3 --> O_LOG["写订单日志"]
end
PENDING -->|"Webhook确认"| CONFIRMED(["CONFIRMED"])
PENDING -->|"超时"| EXPIRED(["EXPIRED
自动取消"])
class V_DONE,CONFIRMED done
class O_BLOCK,O_REJECT,EXPIRED error
class SEARCH,V_EXP,V_RISK,V_CB,V_SUP,V_DEV,V_FREEZE,V_TOKEN,O_TOKEN,O_RECHECK,O_RISK,O_INV,O_CREATE,O_LOCK,O_CREDIT,O_SUP,O_RESULT,PENDING,O_NOTIFY,O_TIMEOUT,O_LOG process
classDef process fill:#1a2744,stroke:#58a6ff,stroke-width:1px,color:#c9d1d9
classDef decision fill:#3a2a1a,stroke:#f0883e,stroke-width:2px,color:#f0883e
classDef done fill:#1a3a2a,stroke:#3fb950,stroke-width:1px,color:#3fb950
classDef error fill:#3a1a1a,stroke:#f85149,stroke-width:2px,color:#f85149
| 数据类型 | 存储位置 | 保留策略 |
|---|---|---|
| 酒店静态数据 | PostgreSQL + ES | 永久 |
| 价格/库存数据 | Redis (热) + PG (冷) | 热数据 5min,历史数据永久 |
| 订单数据 | PostgreSQL | 永久 |
| 查价日志 | ClickHouse | 6 个月 |
| 操作日志 | ClickHouse | 3 个月 |
| 结算数据 | PostgreSQL + ClickHouse | 永久 |
| 合同文件 | MinIO/OSS | 永久 |
查价系统采用 L1 本地内存 + L2 Redis + DB/远程 API 的三级缓存架构,逐层穿透:
请求 → L1 本地内存(sync.Map + LRU,10万条,30s TTL)
→ L2 Redis(单节点/集群,5min TTL)
→ DB / 远程 API
sync.Map + LRU 淘汰策略,容量上限 10 万条,默认 TTL 30s。零网络开销,命中响应 < 1ms。key = price:raw:{supplier_code}:{supplier_hotel_id}:{check_in}:{check_out}
key = price:agg:{hotel_id}:{check_in}:{check_out}:{adults}:{channel_code}
为不同业务维度定义缓存 Tag,支持按维度批量清理:
| Tag | 说明 | 触发场景 |
|---|---|---|
markup:all |
加价规则 | Admin 修改加价规则 |
pricing:{hotel_id} |
动态定价规则 | Admin 修改某酒店定价 |
promo:{hotel_id} |
促销活动 | Admin 创建/修改/删除促销 |
rate:{from}_{to} |
汇率 | 定时任务更新汇率 |
visibility:{agent_code} |
可见性规则 | Admin 修改代理商可见性 |
mapping:{supplier_code} |
供应商映射 | Admin 修改供应商映射关系 |
circuit:{supplier_code} |
熔断状态 | 熔断器状态变更 |
credit:{agent_code} |
信用额度 | Admin 调整代理商额度 |
cache.TagDelete(tags...):批量清除 L2 Redis 中关联该 Tag 的所有缓存 keycache:invalidate:{tag},通知所有实例{"tag": "markup:all", "ts": 1713000000}
| 数据类型 | L1 TTL | L2 TTL | 说明 |
|---|---|---|---|
| 聚合查价结果 | 30s | 5min | 最终兜底缓存 |
| 原始供应商报价 | 10s | 60s/30s | 直连 60s,直采 30s |
| Markup 规则 | 30s | 10min | 变更时 Tag 清理 |
| 动态定价规则 | 30s | 10min | 变更时 Tag 清理 |
| 促销活动列表 | 30s | 5min | 变更时 Tag 清理 |
| 汇率 | 5min | 1h | 定时任务每小时更新 |
| 供应商映射 | 1min | 30min | 变更时 Tag 清理 |
| 可见性规则 | 30s | 10min | 变更时 Tag 清理 |
| 信用额度 | 10s | 1min | 变更时 Tag 清理 |
| 熔断状态 | 10s | 30min | 变更时 Tag 清理 |
缓存 miss 时使用 singleflight 模式:只放一个请求穿透到下游,其余相同 key 的请求等待结果,避免缓存击穿。
查无结果也缓存(空值标记),TTL = 30s。防止恶意请求或无效查询反复穿透到数据库或供应商 API。
所有缓存 TTL 增加 ±10% 随机偏移,避免大量 key 在同一时刻过期导致缓存雪崩。
实际 TTL = 基准 TTL × (0.9 + random() × 0.2)
cache:invalidate{"tag": "markup:all", "ts": 1713000000}cache:invalidate channel