数据模型设计:报价引擎的骨架
做报价引擎,说白了就是在做数据模型的设计。我见过太多团队,一上来就写业务逻辑,结果数据模型没想清楚,后面改得死去活来。嗯,今天我们就聊聊这个核心话题。
核心观点:数据模型决定了报价引擎的扩展性和维护成本。模型设计得好,后面加规则、加商品、加客户分层,就像搭积木一样简单。
商品/服务模型:报价的基础单元
商品模型是报价引擎的「原子」。我习惯把它拆成三层:基础信息层、定价属性层、扩展属性层。
| 层级 | 字段示例 | 说明 |
|---|---|---|
| 基础信息层 | product_id, name, category, unit | 唯一标识、名称、分类、计量单位 |
| 定价属性层 | base_price, cost_price, min_price, max_price | 定价相关的核心数值 |
| 扩展属性层 | attributes (JSON), tags, version | 灵活扩展,比如颜色、尺寸、地区 |
为什么这么分?我在项目中遇到过一个问题:某次客户要求对「同一款商品在不同地区报不同价格」。如果定价属性写死在商品表里,那就得复制商品记录,数据冗余不说,维护起来简直噩梦。后来我把地区相关的属性放到了扩展属性层,用 JSON 存储,配合价格规则模型,轻松搞定。
// 商品模型示例(简化版)
{
"product_id": "PROD_001",
"name": "企业版云服务器",
"category": "compute",
"unit": "台/月",
"base_price": 299.00,
"cost_price": 180.00,
"min_price": 220.00,
"attributes": {
"region": ["cn-east", "cn-south"],
"spec": "8C16G",
"os": ["linux", "windows"]
}
}
我的经验:扩展属性层用 JSON 字段,但一定要加索引。否则查询时全表扫描,性能会崩。我曾经吃过这个亏,后来加了虚拟列 + 函数索引才解决。
价格规则模型:报价的「大脑」
价格规则模型,说白了就是「在什么条件下,怎么算价格」。我把它设计成规则引擎 + 规则参数的组合。
规则引擎负责「条件判断」,规则参数负责「计算逻辑」。举个例子:
// 价格规则模型
{
"rule_id": "RULE_001",
"name": "新客首月5折",
"priority": 10, // 优先级,数字越小越优先
"conditions": { // 条件:客户分层 + 商品分类
"customer_tier": "new",
"product_category": "compute"
},
"action": { // 动作:打折
"type": "percentage",
"value": 50
},
"effective_date": "2024-01-01",
"expiry_date": "2024-12-31"
}
你想想看,如果规则写死在代码里,每次改价都要发版,多痛苦。用这种模型,规则可以动态加载,甚至支持 A/B 测试。
注意:规则优先级一定要设计好。我曾经遇到过一个场景:两条规则同时命中,结果价格算出来是负数。后来我加了一条「规则互斥检查」,才避免了这个坑。
客户分层模型:报价的「瞄准镜」
客户分层模型,决定了「谁该得到什么价格」。我习惯用标签体系 + 分层规则来实现。
- 标签体系:动态打标,比如「高价值客户」「流失风险客户」「新注册用户」
- 分层规则:根据标签组合,映射到具体的价格策略
举个例子:
// 客户分层模型
{
"customer_id": "CUST_001",
"tier": "gold", // 固定分层:silver, gold, platinum
"tags": ["high_value", "long_term", "referral"],
"score": 85, // 客户价值评分
"price_strategy": "premium_discount" // 关联的价格策略
}
为什么用标签体系?因为固定分层太死板了。我记得有一次,一个客户上个月还是「silver」,这个月突然签了大单,如果按固定分层,他得等下个月才能享受优惠。用标签体系,我们可以实时更新标签,报价立刻生效。
报价请求与响应模型:报价的「出入口」
报价请求和响应模型,是报价引擎对外的接口。设计得好,前端和后端对接就顺畅;设计得不好,联调时天天吵架。
我建议把请求模型拆成必填字段和可选字段:
| 字段类型 | 字段名 | 说明 |
|---|---|---|
| 必填 | customer_id, product_id, quantity | 客户、商品、数量 |
| 可选 | coupon_code, promotion_id, custom_params | 优惠券、活动、自定义参数 |
// 报价请求示例
{
"request_id": "REQ_001",
"customer_id": "CUST_001",
"items": [
{
"product_id": "PROD_001",
"quantity": 10,
"custom_params": {
"region": "cn-east",
"duration": 12
}
}
],
"coupon_code": "NEW_USER_50"
}
// 报价响应示例
{
"request_id": "REQ_001",
"status": "success",
"total_price": 1495.00,
"items": [
{
"product_id": "PROD_001",
"unit_price": 149.50,
"original_price": 299.00,
"discount": 50,
"rules_applied": ["RULE_001"]
}
],
"valid_until": "2024-01-31T23:59:59Z"
}
避坑指南:响应模型一定要带上 rules_applied 字段。我曾经遇到客户投诉说「价格不对」,结果查了半天,发现是两条规则同时生效,但前端只显示了一条。加上这个字段后,客户可以清楚看到哪些规则影响了价格,透明度大大提升。
核心逻辑:四层模型如何协同工作
这四个模型不是孤立的,它们像齿轮一样咬合在一起。我画了一张图,帮你理解它们的关系:
从这张图你可以看到:
- 报价请求进来后,先查商品模型拿到基础价格
- 再查客户分层模型拿到客户标签和分层
- 然后交给价格规则模型,根据条件匹配规则,计算最终价格
- 最后组装成报价响应返回给调用方
这个流程看起来简单,但实际落地时有很多细节。比如规则匹配的顺序、缓存策略、并发控制等等。嗯,这些我们后面会慢慢聊。
总结一下:数据模型设计是报价引擎的基石。商品模型提供「是什么」,客户分层模型提供「是谁」,价格规则模型提供「怎么算」,报价请求/响应模型提供「怎么交互」。四个模型各司其职,协同工作,才能支撑起一个灵活、可扩展的报价引擎。