ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Medusa Promotion 促销模块演进全解析:从 2.0 到 2.20 的关键能力与底层实现

Medusa Promotion 促销模块演进全解析:从 2.0 到 2.20 的关键能力与底层实现 Medusa Promotion 促销模块演进全解析从 2.0 到 2.20 的关键能力与底层实现【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa促销模块medusajs/promotion是 Medusa 生态中负责折扣、优惠券、活动预算与买赠等营销能力的独立领域模块在 Medusa 2.0 架构中以可插拔 Module 的形式存在。本文以该模块的 CHANGELOG 为骨架结合 模块源码 与 核心工具枚举定义系统梳理 2.0 至 2.20 版本间促销引擎的能力演进、关键缺陷修复与底层实现原理帮助你理解规则求值、预算控制、并发安全、税含金额计算等核心机制并能在实际项目中准确地配置与排障。模块定位与整体架构按 模块 README 的说明PromotionModule 是 Medusa 的促销引擎它通过一组规则rules约束何时、以何种方式使用优惠码coupon code对购物车进行折扣。从 package.json 可见其身份信息包名medusajs/promotion、当前版本2.20.1、要求 Node.js 20并以medusajs/framework版本一致对齐为 peer dependency。模块的服务入口是 promotion-module.ts它继承MedusaService对外暴露六类实体的 DTOPromotion、ApplicationMethod、Campaign、CampaignBudget、CampaignBudgetUsage、PromotionRule、PromotionRuleValue并提供listActivePromotions、computeActions、registerUsage、revertUsage等核心方法详见下文各节。数据模型方面models 目录核心实体包括Promotion促销主体含code唯一、is_automatic、is_tax_inclusive、typestandard/buyget、statusdraft/active/inactive、limit/used用量限制、metadata并与 Campaign、ApplicationMethod、PromotionRule 关联code上建有带WHERE deleted_at IS NULL的部分唯一索引IDX_unique_promotion_code这也是软删除环境下唯一约束的正确实现方式对应 2.4.0 中唯一约束应把软删除记录考虑在内的修复。ApplicationMethod定义折扣的应用方式——typefixed/percentage、target_typeorder/items/shipping_methods、allocationeach/across/once、value、currency_code、max_quantity、apply_to_quantity、buy_rules_min_quantity等。Campaign 与 CampaignBudget活动及其预算预算按type分为 spend / usage / use_by_attribute 等枚举见 promotion/index.ts2.11.0 起新增attribute字段与 CampaignBudgetUsage 按属性用量明细表。促销类型、目标与分摊方式type / target_type / allocation在创建促销时type决定促销的算法类别。核心枚举定义在 packages/core/utils/src/promotion/index.tsPromotionTypestandard与buyget买赠。ApplicationMethodTypefixed固定金额与percentage百分比。ApplicationMethodTargetTypeorder整单、items商品行、shipping_methods运费。ApplicationMethodAllocationeach逐件、across分摊到全部、once仅一次。在 computeActions 中可以看到标准促销的分发逻辑target_type order时强制使用ACROSS分摊allocationOverrideitems走商品行计算shipping_methods走运费计算而buyget则统一进入 buy-get.ts 的getComputedActionsForBuyGet专用算法。CHANGELOG 中有两条与类型支持直接相关的记录2.7.0percentage value is accounted for in buyget promotions——修复了买赠促销中百分比折扣值未被正确计入的问题。结合 buy-get.ts 的applyPromotionToTargetItems可见FIXED时按value × 数量并以上限applicableAmount单价×数量封顶PERCENTAGE时按applicableAmount × value / 100计算。2.14.0PR #14939support fixed amount discount type in buy-get promotions——买赠促销正式支持固定金额折扣类型。此前买赠仅支持百分比从 2.14.0 起application_method.type fixed也可用于买赠且按上面同一段代码的分支逻辑计算。促销规则体系与规则求值rules / target_rules / buy_rules促销的何时可用由规则PromotionRule控制规则通过PromotionRuleOperatorgte/lte/gt/lt/eq/ne/in见 promotion/index.ts与规则值PromotionRuleValue表达。规则分为三类RuleType作用于整单的rules、作用于目标对象的target_rules、以及买赠场景的buy_rules见 promotion/index.ts。规则求值相关的版本修复集中在运算符语义与空条件上2.9.0in operator work as In instead of equal logic——将in运算符从等值语义修正为真正的包含于集合语义这是促销规则筛选商品/地区/客户分组时的关键行为修正。2.11.0Fix not in promotion rule empty value validation——修复not in规则在空值校验上的缺陷。2.2.0dont evaluate rule condition if conditions to evaluate is empty——当待求值条件为空时跳过规则求值避免空集合上的误判。2.4.0eval conditions for rules are corrected——进一步修正规则条件的求值结果。在 promotion-module.ts 中areRulesValidForContext(promotionRules, applicationContext, ApplicationMethodTargetType.ORDER)负责判断整单级规则是否命中同时会校验application_method.currency_code与购物车币种的一致性对应2.9.0的 check currency when computing actions for promotions 修复。促销状态status与活动时间窗2.3.0PR #10950引入促销状态字段draft/active/inactive。对应 Promotion 模型 中的status枚举字段默认draft带IDX_promotion_status索引。状态与活动时间窗共同决定促销是否对购物车生效核心逻辑在listActivePromotions_promotion-module.ts固定要求status: ACTIVE对于挂载了 Campaign 的促销额外要求starts_at now ends_at任一为空则不限未挂载 Campaign 的促销只要状态为 active 即可。值得注意的是computeActions内部以单个now时间点统一驱动时间窗过滤Ensure we share the same now date across all filters保证同一批次计算的一致性。活动预算体系spend / usage / use_by_attribute预算控制是促销模块最核心的防超卖能力集中在registerUsagepromotion-module.ts与revertUsageL543-L694中。预算类型来自 CampaignBudgetType预算类型含义计数方式spend金额预算累加每次实际折扣金额computedAction.amount超限抛NOT_ALLOWEDusage次数预算每个促销码只计一次promotionCodeUsageMap去重use_by_attribute按属性次数预算2.11.0 起按attribute如customer_id/customer_email维度分别计数spend_by_attribute按属性金额预算枚举中已定义注册逻辑当前聚焦前三类2.11.0按属性限制促销使用次数2.11.0PR #13451support limiting promotion usage by attribute——即use_by_attribute预算类型。实现上在 CampaignBudget 模型 新增attribute字段注释示例customer_id、customer_email并新增usages一对多关联到 CampaignBudgetUsage该表以attribute_valuebudget_id建立部分唯一索引记录每个属性值已使用次数。注册路径registerCampaignBudgetUsageByAttribute_L203-L255在属性值维度上校验limit并累加used回退路径revertCampaignBudgetUsageByAttribute_L257-L291在used 1时删除该行否则递减计算路径computeActions中L877-L917通过getBudgetUsageContextFromComputeActionContext从计算上下文提取customer_id/customer_email调用computeActionForBudgetExceededusage.ts产出CAMPAIGN_BUDGET_EXCEEDED动作若上下文中缺少预算要求的属性值则直接抛INVALID_DATA错误提示缺失。2.12.0促销自身用量上限usage limit2.12.0PR #13760feat: promotion usage limit——促销本身不依赖 Campaign也可设置使用次数上限。对应 Promotion 模型 中的limit可空数字与used默认 0。computeActions中L920-L928当used limit时产出PROMOTION_LIMIT_EXCEEDED动作registerUsage中L405-L419对设置了数字limit的促销递增used并做上限校验。同版本还包含两条配套变更2.12.0PR #14176skip promotion usage limit checks on edit flows——在订单编辑流程中跳过用量限制检查避免编辑已有订单时因已用满而误拦截2.12.0PR #13306Compute virtual adjustments for order previews——为订单预览计算虚拟调整项使预览金额与真实下单一致。另外2.12.0PR #13999为 Promotion 模型 增加metadataJSON 字段since 2.12.0使促销可携带自定义业务数据。2.18.0并发预算守卫serialize concurrent money guards2.18.0是预算安全的关键版本serialize concurrent money guards to prevent over-capture, over-refund and campaign budget overspend。其解决的是经典竞态两个并发请求同时读到相同的used值、同时通过限额校验、同时写入最终导致预算超支或超额退款。在 registerUsage 中可以看到完整的并发保护实现强制事务要求调用必须处于事务中否则抛UNEXPECTED_STATEmust run inside a transaction原因是FOR UPDATE在自动提交连接上会立即释放锁、静默失效行级锁对涉及数字limit的promotion行与涉及的promotion_campaign_budget行执行SELECT ... FOR UPDATE并按id稳定排序加锁以避免并发多促销注册时的死锁锁超时SET LOCAL lock_timeout 3s使锁竞争快速失败而非悬挂锁下重读加锁后以refresh: true重新读取预算用量确保守卫判断基于已提交的最新值。这套机制同时守护了支付领域over-capture / over-refund与促销领域budget overspend的金额一致性。税含促销从 2.8.5 到 2.16.0 的金额基数修复税含tax-inclusive促销的金额计算经历了多轮修复是金额正确性最集中的演进线2.8.5PR #12412引入税含促销能力涉及 promotion、dashboard、core-flows、cart、types、utils、medusa 多个包同期PR #12644修复非可折扣商品non discountable items的检查。2.9.0PR #12960、PR #13106修正折扣计算逻辑与促销税含金额计算Moved calculation logic from total to original_total to ensure consistent base values——将计算基数从total调整为original_total保证基数的一致性与可复算性。2.16.0prevent negative taxable base when stacking tax-inclusive and non-tax-inclusive promotions——这是税含问题的收官修复此前 applied-promotions 累加器把每个促销的调整额存放在各自税基中导致税含促销含税口径去比较前一个非税含促销记录的不含税口径金额时口径错位叠加折扣可能超过行项目价值、把应税基数taxable base推到负数。修复方案见 CHANGELOG 说明已应用金额统一以不含税口径跟踪在被扣除前转换到各促销自身的税基口径且对each与across两种分摊方式均生效。买赠Buy-Get算法与多促销协调买赠促销由 buy-get.ts 实现其核心流程函数注释自述为迭代式应用preparePromotionApplicationState从剩余买量中选择满足buy_rules_min_quantity的买项、从剩余目标量中挑选目标项、并以max_quantity/apply_to_quantity约束应用数量applyPromotionToTargetItems按单价计算折扣额fixed 按件、percentage 按百分比检查预算上限更新跨促销协调映射表updateEligibleItemQuantities扣除已消费数量防止同一商品被重复套用循环直至无法满足条件并设有MAX_PROMOTION_ITERATIONS 1000的防死循环安全阀。该文件还体现了两个关键细节稳定排序sortByPrice对等额商品返回 0避免不稳定比较器导致是否命中促销取决于行顺序对应注释说明的排序一致性问题跨促销协调methodIdPromoValueMap记录每个商品行已累计的促销金额calculateRemainingQuantities计算其他促销已占用的数量确保多个促销叠加时不会超出商品价值。多促销应用的排序由sortByBuyGetTypebuy-get.ts与 promotion-module.ts 中的查询排序共同决定买赠优先、application_method.value降序买赠之间再按buy_rules_min_quantity、apply_to_quantity降序。2.7.0PR #11992修复了多个百分比促销未全部应用multiple percentage promotions werent applied的场景即多百分比叠加时的计算协调问题2.6.0/2.5.1/2.5.0等版本则主要是工程清理。自动促销与性能优化演进2.10.0免运费促销free shipping2.10.0PR #13263在 dashboard、core-flows、js-sdk、link-modules、promotion 中联动支持免运费促销同期PR #13294清理了旧的无用促销代码库。免运费的本质是target_type shipping_methods的标准促销计算路径见 promotion-module.ts 的getComputedActionsForShippingMethods。自动促销预过滤2.7.0 → 2.10.3 → 2.11.0自动促销is_automatic不需要显式输入优惠码即可生效但其规则求值需要把整张购物车带入计算开销较大。性能优化分三步演进2.7.0PR #12129Improve performances [1]第一轮促销计算性能优化2.10.3PR #13524promo prepare top level rules filter将顶层规则top-level rules下推为数据库查询过滤条件在 SQL 层先筛掉不可能命中的自动促销对应 build-promotion-rule-query-filter-from-context2.10.3PR #13540Prevent promotion filtering to exceed psql limits防止预过滤生成的 SQLIN条件数量超过 PostgreSQL 参数上限2.11.0进一步改进预过滤Further promotions pre filtering improvements。在 computeActions 中可以看到该机制非禁用自动促销时先从应用上下文构建规则过滤条件预查出候选自动促销 id再与显式传入的促销码取并集$or。基础设施与工程演进mikro-orm 6、迁移命令与依赖治理CHANGELOG 后半段记录了模块底座的持续工程化2.0.0PR #7341随 Medusa 2.0 大版本发布促销模块作为独立模块正式落地2.0.x–2.1.x期间完成模块迁移重构2.1.2 migrate promotion module、MikroORM CLI 包装修复2.0.5、移除促销后同步清除购物车调整项2.1.1 updating cart with removed promotion removes adjustments等。2.4.0PR #10292升级到mikro-orm 6并同步修复软删除下的唯一约束问题。2.5.0PR #11216AbstractModuleService的create方法类型安全化。2.6.1PR #11738移除 Medusa 包的版本区间ranges改为精确版本对齐2.11.0PR #13439进一步把 peer deps 收敛为单一包并从 framework 统一 re-export。2.12.2PR #14262migrate 命令新增all-or-nothing参数使迁移要么全部成功要么全部回滚2.12.3PR #14315修复迁移生成器的 import。2.13.0minor bump2.17.2PR #15683补充包 bugs 元数据。模块内提供完整的 migrations 目录从Migration20240227120221到Migration20251107050148共 16 个并在 package.json 中暴露migration:initial、migration:create、migration:up、migration:down、orm:cache:clear等基于medusa-mikro-ormCLI 的迁移命令。测试佐证与可验证性模块配套了覆盖核心能力的集成测试integration-testspromotion.spec.ts促销 CRUD 与规则校验compute-actions.spec.ts各类促销的计算动作输出campaign.spec.ts活动与预算行为register-usage.spec.ts / revert-usage.spec.ts预算注册与回退evaluate-rule-value-condition.spec.ts规则值条件求值。运行方式package.jsonyarn test执行单元测试yarn test:integration执行集成测试。小结从packages/modules/promotion/CHANGELOG.md的版本脉络可以看到Medusa 促销模块在 2.x 系列中的演进主轴清晰先完成 2.0 模块化落地类型安全、mikro-orm 6、依赖治理再补齐业务能力状态机、促销用量上限、按属性预算、免运费、税含促销最后集中攻坚正确性并发预算守卫、税基口径统一、买赠固定金额支持与性能规则预过滤下推、SQL 上限规避。理解这条演进线不仅有助于你在当前版本中正确配置促销规则与预算也能在排查折扣未生效预算被超支金额基数异常等问题时快速定位到对应的实现文件与修复意图。【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表