---
name: extension-slots
description: MallBase 扩展槽边界规范；涉及会员、积分、分销、余额、优惠等权益扩展接入订单、商品编辑或 UniApp 展示时使用。
---

# 扩展槽边界规范

## 适用场景

当需求涉及以下任一方向时，优先使用本规则：

- 订单金额计算新增权益项，例如会员优惠、积分抵扣、优惠券、余额权益、分销相关金额。
- 订单状态流转新增副作用，例如支付后冻结、完成后发放、关闭后回滚、退款后扣回。
- 后台商品编辑页新增营销字段或 SKU 级权益字段。
- UniApp 商品详情、规格弹窗、订单确认页新增权益展示。
- 现有会员、积分、分销、余额能力需要接入统一扩展点。

## 后端规则

1. 订单金额扩展优先实现 `OrderPriceContributorInterface`，通过 `OrderPricePipeline` 接入。
2. 订单状态副作用优先实现 `OrderEventListenerInterface`，通过 `OrderEventDispatcher` 接入。
3. 新扩展默认注册在 `ExtensionRegistry`，不要把新权益逻辑直接堆回 `OrderService`、`OrderStatusMachine` 或 `RefundOrderStatusMachine`。
4. 扩展实现必须保持 Service 无状态，不能依赖实例属性保存请求态。
5. 扩展中的落库副作用必须幂等，尤其是支付、完成、关闭、退款等可重复触发的事件。
6. 金额扩展必须以分为边界做精确分配，避免浮点误差；返回给前端的金额统一保留两位小数。
7. 多个扩展同时作用于订单金额时，必须明确优先级，避免互相覆盖或重复扣减。
8. 不要为了单次、局部、不会复用的逻辑强行新增扩展点；局部简单逻辑可以留在原模块内。

## 后台商品编辑规则

1. 商品营销字段、SKU 扩展字段优先集中在 `frontend/admin/apps/web-antd/src/views/goods/goods/extension-slots.ts`。
2. `goods-edit.vue` 和 `useGoodsEdit.ts` 只负责渲染、透传、批量应用、提交转换，不再散落具体权益字段判断。
3. 新增 SKU 字段必须同时覆盖默认值、编辑回填、批量设置、提交转换和列显隐。
4. 字段显隐必须由功能开关和当前表单模式共同决定，避免功能关闭后仍提交隐藏字段。

## UniApp 规则

1. 商品详情、规格弹窗、订单确认页的权益展示优先集中在 `frontend/uniapp/utils/extension-slots.js`。
2. 展示层只消费扩展槽返回的展示项，避免每个页面重复判断会员价、积分赠送、成长值等细节。
3. 订单确认页的权益卡片、商品行提示、价格汇总行应从同一扩展状态构建，避免展示和提交金额不一致。
4. 新增可交互权益项时，必须明确默认状态、禁用条件、提交参数和接口预览结果的对应关系。

## 测试要求

1. 后端新增价格贡献器或事件监听器时，至少补充管线或监听器单测。
2. 影响订单主链路时，必须覆盖预览、创建、支付、完成、关闭或退款中的相关边界。
3. 影响前端展示时，至少执行对应前端构建；涉及真实用户链路时，用本地接口和浏览器抽样验证。
4. 如果只调整扩展槽结构但不改变行为，测试可以聚焦在原有合同不变和关键页面不报错。

## 反模式

- 在 `OrderService::calculateAmounts()` 中继续追加会员、积分、分销、优惠券等分支。
- 在状态机里直接调用具体业务 Service，绕过统一事件分发。
- 前端多个页面分别复制同一套会员价、积分赠送、成长值判断。
- 功能开关关闭后仍显示字段、提交字段或触发副作用。
- 为一次性页面排版创建新的扩展槽，增加不必要复杂度。
