会员权益 — 技术设计(商城 oemsaas)

状态:与业务需求 requirements.md 对齐;配置 UI 在 oemsaasapp表在商城
冲突处理:与代码冲突时以代码为准;表结构以本文 + docs/sql/membership_tables.sql 为准。

1. 职责边界

职责
oemsaasapp配置界面;升降级规则表(应用库);订阅 orders/paid Webhook(延迟 5 分钟);计算级别并回写 customer.level_id
oemsaas 商城表存储与运行时读表;积分加权;会员价(购物车/结账/前台);运费会员日+级别过滤;o_customer_level CRUD

配置不下发,商城运行时直接查 MySQL(配合 Redis 缓存可选,后续实现阶段再定)。


2. 数据表一览

存储表 / 位置说明
升降级规则应用库 o_membership_upgrade_rulelevel_id=0 店铺行 + >0 级别行)design-app.md
应用安装 / Webhook商城 o_my_appo_webhook应用库不建表
积分加权o_point_rule.member_points_weightJSON,按 level_id
会员日o_store_config key=membership_member_dayJSON
会员折扣o_customer_level_product_discount(新建)product_id=0 全场;>0 指定商品
运费扩展o_shipping_discount.member_day_only是否仅会员日有效
运费适用级别o_shipping_discount_customerobj_type=customer_level
顾客级别o_customer_level + customer.level_id已有

3. 表结构说明

3.1 o_customer_level_product_discount(新建)

字段类型说明
idint unsigned PK
store_idint unsigned店铺
level_idint unsignedo_customer_level.id
product_idint unsigned0 = 全场;>0 = 指定商品
discountdecimal(5,2)平常日支付比例 %(100=原价,80=8 折)
member_day_discountdecimal(5,2)会员日支付比例 %
created_at / updated_atint unsigned

约束:

  • UNIQUE KEY uk_store_level_product (store_id, level_id, product_id)
  • 每级别指定商品 ≤ 9 条(product_id > 0),由应用/API 校验
  • 每级别至少 0~1 条 product_id=0 表示全场默认
  • 指定商品折扣优先于全场(同 level_id 下命中 product_id 则不用 0 行)

3.2 o_point_rule.member_points_weight(新增字段)

类型:varchar(4096),JSON。

{
  "enabled": 1,
  "levels": [
    {
      "level_id": 1,
      "normal_percent": 100,
      "member_day_percent": 110
    },
    {
      "level_id": 2,
      "normal_percent": 100,
      "member_day_percent": 120
    }
  ]
}
字段说明
enabled0 关闭加权;1 开启
levels[].level_ido_customer_level.id 对应
normal_percent / member_day_percent在现有积分规则应得积分上乘以 percent/100;结果 保留 1 位小数向上取整

应用写配置;商城 PointsBalanceService::orderPaid() 读。

3.3 会员日 — o_store_config

Keymembership_member_day(常量建议:StoreConfigModel::KEY_MEMBERSHIP_MEMBER_DAY

Value:JSON(≤4096 字符,与字段长度一致)

{
  "weekday_rules": [
    {
      "frequency": "weekly",
      "weekdays": [1, 2, 5]
    },
    {
      "frequency": "first_week",
      "weekdays": [6]
    },
    {
      "frequency": "last_week",
      "weekdays": [7]
    }
  ],
  "month_dates": [1, 8, 15, 28]
}
字段说明
frequencyweekly 每周 / first_week 每月首周 / last_week 每月末周
weekdays1=周一 … 7=周日,多选
month_dates每月 1–28 号,多选

判定:

  • 使用店铺时区 o_store_config.time_zone(已有 KEY_STORE_TIME_ZONE
  • 当日命中 weekday_rules 任一条month_dates 任一号 即为会员日
  • 实现:MembershipMemberDayService::isMemberDay(int $storeId, ?int $timestamp = null): bool

3.4 运费 — o_shipping_discount

新增字段:

字段类型默认说明
member_day_onlytinyint unsigned00=活动时段内始终有效;1=活动时段内仅会员日当天对命中级别顾客生效

3.5 运费 — o_shipping_discount_customer

扩展 obj_type 枚举,增加 customer_level

  • obj_id = 字符串形式的 level_id(与现有 customer/tag 用法一致)
  • 一条运费活动可关联多个级别(多行)

ShippingDiscountService 过滤逻辑(实现阶段):

  1. 若配置了 customer_level 行,则当前顾客 level_id 须在关联列表中
  2. member_day_only=1 且当日非会员日 → 该活动不生效
  3. 其余仍走现有 tag/customer/visitor/all 逻辑

4. Webhook:升级(应用侧)

约定
事件orders/paid(已有 OrderHandler + 全量订单体)
延迟delay_time = 300(5 分钟),避免 ES/从库延迟导致应用读不到最新 total_spent
注册应用安装时在 o_webhook 写入;delay_step 与现网其它 Webhook 一致
回写应用调商城 api / openapi / mixapi 更新 customer.level_id

商城实现自动升级 Listener。


5. 商城运行时挂点(实现阶段)

能力挂点读表
购物车/结账会员价CartService::cartDataComposition()o_level_product_discount + 会员日
前台商品价展示ProductService / Liquid 变量同上
积分加权PointsBalanceService::orderPaid()o_point_rule.member_points_weight
运费ShippingDiscountServicemember_day_only + customer_level 关联
会员日公共 Serviceo_store_config.membership_member_day

应用门禁MyAppService::isInstall('app_xxx')(auth_key 与应用中心对齐后补常量)。


6. 配置写入(应用 → 商城)

应用通过 api/openapi/mixapi 调用商城 Service,无商城配置 UI

配置写入方式
积分加权现有 POST /pointsPoints/addOrUpdate),字段 member_points_weight
会员日现有 StoreConfig/updatesetConfig,key=membership_member_day
会员折扣PUT /membership/discount/:level_id
运费现有运费 API + member_day_only + member_level_ids
升降级不写商城表

7. 需求完整性检查

7.1 已覆盖(可进入 DDL + 开发)

  • 表在商城、应用写、商城读
  • 升降级在应用 + Webhook 5 分钟延迟
  • 积分 JSON 按级别 + 会员日
  • 会员日 store_config JSON(星期 + 月日)
  • 会员折扣表 + 全场/指定商品
  • 运费会员日 + 级别
  • 购物车/结账会员价(实现阶段)
  • 支付成功积分(实现阶段)

7.2 不阻塞表设计、实现阶段再定

说明
会员价 vs diy_offer/promotion/coupon 顺序原型未定义,Cart 接入时与产品确认
商品详情页会员价与列表共用 o_level_product_discount,字段一致
五种结账形态 COD 是否全开promotions-by-checkout-type.md 对齐逐形态接入
应用 auth_key 常量名与应用中心注册名一致后补代码常量
discount 是否允许 0建议 (0, 100],0 表示免费需产品确认

8. 部署

各节点业务库执行:

# 见 docs/sql/membership_tables.sql

执行后同步更新 Model $schemaPointsRuleModelShippingDiscountModelShippingDiscountCustomerModel,新建 CustomerLevelProductDiscountModel)。


9. 应用库(oemsaasapp)

升降级规则、升级流水在 应用独立库(2 张表);安装与 Webhook 在商城 o_my_app / o_webhook


10. 变更记录

日期说明
2026-06-08初版:表结构、JSON 约定、Webhook 延迟、商城挂点
2026-06-08补充应用库文档链接