电商价格系统怎么设计?一次讲清一口价、活动价、券后价、价格快照与改价留痕
电商价格系统怎么设计?一次讲清一口价、活动价、券后价、价格快照与改价留痕
大家好,我是一名有 4 年工作经验的 Java 后端开发。
电商系统里,很多人一开始会觉得价格就是商品表里的一个字段,但真正做起来会发现,价格系统几乎会牵扯商品、营销、订单、优惠券、结算、审计一整条链路。
这篇文章我想系统聊一聊,电商价格系统到底应该怎么设计。
🦅个人主页
🐼
文章目录
- 电商价格系统怎么设计?一次讲清一口价、活动价、券后价、价格快照与改价留痕
- 一、前言
- 二、价格系统里最容易混的几个概念
- 三、推荐的整体设计思路
- 3.1 基础价格层
- 3.2 营销规则层
- 3.3 订单结算层
- 四、数据库怎么设计
- 4.1 SKU 基础价格表
- 4.2 价格变更记录表
- 4.3 订单价格快照
- 五、结算时怎么计算价格
- 六、为什么一定要有价格快照
- 七、最容易踩的坑
- 7.1 把最终支付价直接写回商品表
- 7.2 没有价格日志
- 7.3 下单不落快照
- 7.4 优惠分摊不清晰
- 八、面试中怎么回答
- 九、总结
- 十、结尾
一、前言
很多项目最开始的商品表大概都会有这种字段:
product.id product.name product.price刚开始看起来很简单,但业务一复杂,问题马上就来了:
- 原价、售价、活动价到底存哪个?
- 会员价放哪?
- 满减、优惠券、平台补贴算谁的价格?
- 价格改了以后,历史订单按哪个价格算?
- 商品详情页显示价、购物车预估价、下单结算价为什么可能不一样?
所以价格系统真正要解决的,不是“多少钱”,而是:
在不同场景下,当前价格从哪里来、怎么算、如何留痕、如何和订单结算对齐。
二、价格系统里最容易混的几个概念
我建议至少先分清这些价格:
吊牌价 / 原价销售价活动价会员价券后价结算价
其中最关键的一点是:
用户最终支付价格,并不一定应该直接落在商品表里。
很多时候商品表里适合存的是:
- 基础销售价
而最终结算价要通过规则实时计算。
三、推荐的整体设计思路
我更建议价格系统按三层来拆:
3.1 基础价格层
描述 SKU 的基础价格:
- 原价
- 售价
3.2 营销规则层
描述额外变化:
- 活动价
- 限时折扣
- 满减
- 会员价
- 平台补贴
3.3 订单结算层
最终在下单时计算:
- 本次成交价
- 优惠分摊
- 实付金额
这样拆的好处是:
- 商品价格和营销规则解耦
- 历史订单价格可追溯
- 改价和活动结束后不会污染订单历史
四、数据库怎么设计
4.1 SKU 基础价格表
CREATETABLEsku_price(sku_idBIGINTPRIMARYKEY,origin_priceDECIMAL(10,2)NOTNULL,sale_priceDECIMAL(10,2)NOTNULL,currencyVARCHAR(8)NOTNULLDEFAULT'CNY',updated_atDATETIMENOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP);4.2 价格变更记录表
CREATETABLEsku_price_log(idBIGINTPRIMARYKEYAUTO_INCREMENT,sku_idBIGINTNOTNULL,before_origin_priceDECIMAL(10,2)DEFAULTNULL,before_sale_priceDECIMAL(10,2)DEFAULTNULL,after_origin_priceDECIMAL(10,2)DEFAULTNULL,after_sale_priceDECIMAL(10,2)DEFAULTNULL,operator_idBIGINTDEFAULTNULL,reasonVARCHAR(255)DEFAULTNULL,created_atDATETIMENOTNULLDEFAULTCURRENT_TIMESTAMP);4.3 订单价格快照
订单明细里建议直接落快照:
CREATETABLEorder_item(idBIGINTPRIMARYKEYAUTO_INCREMENT,order_idBIGINTNOTNULL,sku_idBIGINTNOTNULL,sku_nameVARCHAR(128)NOTNULL,sale_priceDECIMAL(10,2)NOTNULL,final_priceDECIMAL(10,2)NOTNULL,discount_amountDECIMAL(10,2)NOTNULLDEFAULT0,quantityINTNOTNULL);这一步特别重要,因为:
商品未来价格可以变,但历史订单价格不能跟着漂。
五、结算时怎么计算价格
我更建议按这个顺序算:
- 取 SKU 基础销售价
- 应用活动价或会员价
- 计算店铺优惠 / 平台优惠
- 计算优惠券分摊
- 落订单快照
也就是说:
订单价格不是查一个字段,而是一条计算链。
六、为什么一定要有价格快照
如果没有价格快照,后面会出现很多问题:
- 商品改价后,历史订单无法对账
- 售后退款不知道按哪个价格退
- 财务对账口径不一致
- 用户投诉时拿不出当时成交价依据
所以我建议:
- 商品表存当前价格
- 订单表存成交快照
- 日志表存改价记录
七、最容易踩的坑
7.1 把最终支付价直接写回商品表
这会把商品价格和订单价格彻底混乱。
7.2 没有价格日志
后面很难知道谁改了价格、为什么改。
7.3 下单不落快照
历史订单价格无法稳定复盘。
7.4 优惠分摊不清晰
平台补贴、店铺让利、优惠券抵扣混在一起,后面结算很容易扯不清。
八、面试中怎么回答
如果面试官问你:
电商价格系统一般怎么设计?
你可以这样回答:
第一,我不会把价格简单理解成商品表里的一个字段,而会把价格拆成基础价格、营销规则和订单结算三层。商品表更多承载基础销售价,活动价、会员价、优惠券等规则在结算时动态参与计算。
第二,订单下单时我一定会落价格快照,包括原销售价、优惠金额和最终成交价,因为商品价格未来可能变化,但历史订单价格必须可追溯。
第三,我还会单独设计价格变更日志表,记录谁改了价格、改前改后是多少、为什么改,这样后续排障、审计和结算都更清楚。
九、总结
价格系统真正难的,不是“字段怎么命名”,而是如何把:
- 当前商品价格
- 营销规则
- 历史成交价
- 改价留痕
这几层真正拆开。
如果只记一句结论,我觉得可以记住这句:
商品表适合存当前价格,订单表必须存成交快照,价格规则和价格日志不能混在一起。
十、结尾
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注。
后面我会继续整理一些更偏实战的 Java 后端和电商系统设计文章。
