资讯动态

餐饮 SaaS 优惠券系统架构演进(一):从“券配置”到“用户权益”——领域模型与数据建模复盘

发布时间:2026/10/3 7:33:24 来源:尧图企业网站定制
本文基于真实后端代码做工程复盘。业务标识、账号、域名、门店/商户数据均做脱敏类名与通用表名用于说明真实技术结构。本文重点不是教你“建一张 coupon 表”而是解释为什么优惠券一旦进入生产系统就必须区分模板、范围、用户权益、规则快照与订单计价。1. 背景与问题优惠券为什么很快就不再是 CRUD最初需求通常只是“配置一张券、用户领取、下单减钱”。但当前项目已经支持折扣券、满减券、商品兑换券并进一步叠加门店范围、场景范围、商品白名单/黑名单、分类范围、会员领取限制、转赠、周期使用、分享与首页弹窗等能力。维度当前代码中的代表字段/对象为什么会变复杂券类型DiscountCoupon / FullReductionCoupon / ProductExchangeCoupon不同优惠动作需要不同计算模型适用范围storeScope / sceneMask / productScopeType一张券并不是对整个订单无条件生效发放限制totalIssueCount / perUserReceiveLimit / audienceType领取本身就是受约束的权益生成历史规则UserClaim.couponSnapshot模板会变但已经发给用户的权益不能跟着漂移订单关联usedOrderId / orderSerialNumber / status券进入订单后有独立生命周期2. 目标与约束这套模型实际需要同时满足五个目标可配置、可领取、可计算、可追溯、可演进。其中最关键的约束是运营配置允许修改但历史用户权益和订单计价口径必须稳定。图 1当前优惠券领域架构基于真实代码关系抽象3. 真实领域模型三类券不是一个“大而全 coupon 表”当前项目没有使用单表 type 字段承载所有规则而是为三类券分别建立模板表和用户领取表。这种设计牺牲了一部分公共代码复用但换来了字段语义清晰和类型隔离。券类型模板实体 / 表用户权益实体 / 表核心优惠动作折扣券DiscountCoupon / discount_couponDiscountCouponUserClaim / discount_coupon_user_claim按折扣率计算可限制最大优惠数量/金额满减券FullReductionCoupon / full_reduction_couponFullReductionCouponUserClaim / full_reduction_coupon_user_claim达到门槛后减固定金额商品兑换券ProductExchangeCoupon / product_exchange_couponProductExchangeCouponUserClaim / product_exchange_coupon_user_claim兑换指定商品/规格4. ERD模板、范围关系与用户领取实例图 2折扣券表族 ERD其余两类券采用相同模式这里需要纠正一个很容易误判的点项目没有独立的 coupon_snapshot 表。coupon_snapshot是用户领取表上的 JSON 字段由DiscountCouponSnapshotTypeHandler/ 对应 TypeHandler 完成对象与 JSON 的映射。5. 为什么快照必须跟着用户券走在DiscountCouponUserClaim中couponSnapshot的注释明确写着“在领取时固化用于后续展示与使用逻辑按照领取时的规则执行”。领取实现会把门店 ID、商品白/黑名单、分类 ID、门槛、折扣率、最大优惠、使用周期等一起复制进快照。图 3模板修改与历史用户权益之间的隔离机制领取时的数据写入顺序1. SELECT discount_coupon ... FOR UPDATE 2. 校验 status / canUserClaim / activity time / audience 3. 校验每人限领次数、总发行上限 4. 计算 validStartTime / validEndTime 5. 查询 store/product/category 关系 6. 构建 DiscountCouponRuleSnapshot 7. INSERT discount_coupon_user_claim(coupon_snapshot...) 8. 发布用户券过期延迟消息6. 方案取舍为什么当前设计“合理但开始出现演进压力”图 4当前数据模型的主要收益与技术债从 Code Review 看当前建模已经解决了“模板与权益混淆”这个最关键的问题但新的边界问题已经出现例如DiscountCoupon同时包含 popupEnabled、popupImageUrl、shareImageUrl、shareTitle 等展示字段说明营销展示能力正在向券模板实体渗透。7. Troubleshooting / RCA为什么修改模板不能影响已领券Symptoms典型症状是运营后台修改了券的门槛、折扣或适用范围后历史用户在券包看到的说明或下单优惠发生变化。Investigation排查重点不是“前端缓存”而是确认展示和计价到底读取模板当前值还是读取user_claim.coupon_snapshot。当前实现中用户券 VO 与优惠计算服务都已经优先从领取快照读取规则。Root Cause如果系统直接以模板作为唯一事实来源本质上是把“可变配置”误当成“已经授予用户的业务事实”。Resolution领取时固化快照订单计价与用户券展示读取快照模板只控制未来领取实例。Verification修改模板后验证两组用户修改前已领取用户保持旧规则修改后新领取用户获得新规则订单计算与券包展示应分别与各自快照一致。Lessons Learned配置数据 ≠ 业务事实。只要一个配置会影响已经产生的订单、权益、合同或结算结果就应该考虑版本化或快照化。8. 后续优化方向当前信号建议展示职责拆分coupon 实体已包含 popup/share 字段考虑 CouponDisplayConfig / CampaignPresentation 边界避免模板继续膨胀公共能力抽象三类券存在大量同构字段和同构 Service先抽公共领域接口与策略不急于合并数据库表快照兼容性JSON TypeHandler 强依赖 DTO 结构快照增加 schemaVersion变更字段保持向后兼容审计目前核心事实分散在 claim/status/order 关联中补充统一权益操作日志或事件流水9. 总结当前优惠券系统的核心价值不在“支持三种券”而在于已经形成了模板配置 → 适用范围 → 用户领取实例 → 规则快照 → 订单计价的业务事实链。后面的并发、计价、补偿和营销平台化都是建立在这个数据模型之上。下一篇《餐饮 SaaS 优惠券系统架构演进二领券链路——行锁、限领、快照、过期消息与批量发券》

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑