Files
hfb_sys/docs/租客成长等级与主动短信触达方案.md
T
2026-07-28 14:45:33 +08:00

9.2 KiB
Raw Blame History

租客成长等级与主动短信触达方案

一、租客成长等级

功能范围

  • 只统计租号订单,即 rental_orders
  • 不统计撞车订单,即 mohong_orders
  • 积分只按已完成租号订单的实付租金累计,不含押金。
  • 等级三档:铂金、钻石、巅峰。
  • 下单租号时按用户当前等级使用折扣。
  • 折扣和升级门槛后台可配置。
  • 历史订单保存价格、等级、折扣快照,后台修改配置不影响历史订单。

积分规则

推荐规则:

成长积分 = 已完成租号订单实付租金金额
1 元实付租金 = 1 积分

不建议统计押金,原因是押金可能退款、免押、暂扣或赔付,直接计入成长积分会让财务口径变复杂。

积分发放时机建议放在租号订单完成后,而不是支付成功后。这样可以避免用户付款后退款、取消或关闭订单导致刷积分。

后台配置

使用现有 system_configs,新增配置项:

renter.growth_level_rules

建议默认值:

{
  "enabled": true,
  "points_per_yuan": 1,
  "levels": [
    { "code": "platinum", "name": "铂金", "min_points": 300, "discount_bps": 9900 },
    { "code": "diamond", "name": "钻石", "min_points": 1000, "discount_bps": 9800 },
    { "code": "peak", "name": "巅峰", "min_points": 5000, "discount_bps": 9500 }
  ]
}

discount_bps 使用万分比,避免小数误差。例如 9800 表示 9.8 折。

数据库字段

users 建议新增:

  • renter_growth_points:租客成长积分。
  • renter_growth_level:当前租客成长等级。

rental_orders 建议新增:

  • rent_original_amount_cent:原始租金。
  • rent_discount_amount_cent:租金优惠金额。
  • renter_growth_level:下单时命中的等级编码。
  • renter_growth_level_name:下单时命中的等级名称。
  • renter_discount_bps:下单时命中的折扣。
  • growth_points_awarded:本单已发放积分,用于防重复。

下单折扣

在订单创建阶段应用折扣,支付阶段不重新计算。

现有租号订单创建位置:

backend/internal/modules/order/lifecycle.go

现有支付金额来自:

backend/internal/modules/payment/payment_start.go
amountCent := row.RentAmountCent + row.DepositAmountCent

因此订单创建时应直接把 RentAmountCent 写成折后租金,后续支付、退款、结算、仲裁都沿用订单金额。

推荐折扣范围:

  • 只折扣租金。
  • 押金不参与折扣。
  • 折扣优先扣平台费,不影响号主收入。
  • 如果折扣金额超过平台费,第一版建议限制为最多扣到平台费为 0,避免平台费出现负数。

自动升级

租号订单首次完成时:

  1. 校验 growth_points_awarded = 0,避免重复发放。
  2. 根据折后租金计算本单积分。
  3. 累加到 users.renter_growth_points
  4. 根据 min_points 自动计算用户当前等级。
  5. 更新 users.renter_growth_level
  6. 回写 rental_orders.growth_points_awarded

默认只升级不降级。若后台未来调整门槛,可以选择只影响后续升级判断,不主动批量降级。

前端范围

后台系统配置页:

  • 配置是否启用。
  • 配置每元积分。
  • 配置三个等级的门槛积分。
  • 配置三个等级的折扣。

后台用户管理:

  • 展示成长等级。
  • 展示成长积分。
  • 支持后台调整积分或等级,操作写审计日志。

用户端订单页:

  • 展示租金原价。
  • 展示等级折扣。
  • 展示优惠金额。
  • 展示折后租金。
  • 展示押金。
  • 展示应付合计。

测试范围

  • 铂金、钻石、巅峰不同等级折扣计算正确。
  • 押金不打折。
  • 折扣不影响号主收入。
  • 支付金额等于折后租金加实付押金。
  • 撞车订单完成不增加成长积分。
  • 租号订单完成只发放一次成长积分。
  • 积分达到门槛后自动升级。
  • 后台修改配置不影响历史订单快照。

二、主动短信触达

业务目标

用户完成一笔租号订单后,平台可以主动发送短信,告知用户本月已经累计完成多少笔租号订单,以及距离领取奖励还差多少笔。

示例文案:

尊敬的 用户名:
您好,您已经在 大锤商行 累计完成 1 笔租号订单,再完成 x 笔即可领取 xxxx。

统计口径

  • 只统计 rental_orders
  • 只统计状态为 completed 的租号订单。
  • 不统计 mohong_orders 撞车订单。
  • 按自然月统计,建议使用订单完成时间 settled_at
  • 月统计区间为当前月第一天 00:00:00 到下月第一天 00:00:00。

统计 SQL 口径示例:

SELECT COUNT(*)
FROM rental_orders
WHERE renter_id = ?
  AND status = 'completed'
  AND settled_at >= ?
  AND settled_at < ?;

触发时机

推荐触发点:租号订单完成后。

现有完成入口:

backend/internal/modules/order/checkout_finalize.go

finalizeCheckout 会把订单状态改为 completed。短信不建议在事务内直接发送,避免短信服务失败导致订单完成失败。

推荐流程:

  1. 订单完成事务内写入短信待发送记录。
  2. 事务提交后由后台任务发送短信。
  3. 发送成功、失败、重试次数都落库。

短信通道

项目已有短信集成:

backend/internal/integrations/sms

当前 Provider 只支持登录验证码:

SendLoginCode(ctx context.Context, phone string, code string) error

主动短信需要扩展为模板短信能力,例如:

SendTemplate(ctx context.Context, phone string, templateCode string, params map[string]string) error

阿里云短信实际发送不能随意拼接任意文案,必须使用已审核的短信模板。因此后台可配置“模板 code、奖励规则、预览文案”,真实发送时只传模板变量。

后台配置

使用 system_configs,新增配置项:

sms.rental_completion_reminder

建议默认值:

{
  "enabled": false,
  "brand_name": "大锤商行",
  "template_code": "",
  "monthly_user_limit": 4,
  "cooldown_hours": 24,
  "rewards": [
    { "target_orders": 3, "reward_name": "成长礼包", "enabled": true },
    { "target_orders": 5, "reward_name": "专属优惠券", "enabled": true }
  ],
  "preview_template": "尊敬的 {{nickname}}:您好,您已经在 {{brand_name}} 累计完成 {{completed_count}} 笔租号订单,再完成 {{remaining_count}} 笔即可领取 {{reward_name}}。"
}

规则说明:

  • enabled:总开关。
  • brand_name:短信里展示的品牌名。
  • template_code:阿里云审核通过的营销或通知短信模板 code。
  • monthly_user_limit:单用户单月最多发送次数。
  • cooldown_hours:同一用户两次主动短信最小间隔。
  • rewards:奖励门槛,从小到大配置。
  • preview_template:后台预览使用,真实发送以短信供应商模板为准。

计算 x 的规则:

  1. 获取用户本月完成租号订单数 completed_count
  2. 找到第一个 target_orders > completed_count 的奖励。
  3. remaining_count = target_orders - completed_count
  4. 如果没有下一个奖励,默认不发送,或后续配置为发送达成提醒。

数据库表

建议新增短信发送记录表:

sms_messages

字段建议:

  • id
  • user_id
  • phone
  • biz_type:例如 rental_completion_reminder
  • biz_id:租号订单 ID。
  • template_code
  • template_params
  • content_preview
  • statuspendingsentfailedcancelled
  • retry_count
  • last_error
  • sent_at
  • created_at
  • updated_at

建议加唯一索引:

uk_sms_messages_biz_type_biz_id_user

用于保证同一笔完成订单不会重复创建短信任务。

发送任务

新增后台任务,例如:

backend/internal/jobs/smsdispatch

任务逻辑:

  1. 查询 pending 且重试次数未超限的短信。
  2. 调用短信 provider 发送模板短信。
  3. 成功后标记 sent,写 sent_at
  4. 失败后记录 last_error,增加 retry_count
  5. 超过最大重试次数后标记 failed

限流与防打扰

发送前需要校验:

  • 系统配置已启用。
  • 用户手机号有效。
  • 本订单尚未创建过同类短信。
  • 用户本月主动短信次数未超过 monthly_user_limit
  • 用户距离上次主动短信超过 cooldown_hours
  • 找得到下一个奖励门槛。

第一版可以先不做用户退订,但如果短信属于营销性质,建议后续加用户短信订阅状态或退订名单。

前端范围

后台系统配置页:

  • 开关。
  • 品牌名。
  • 模板 code。
  • 单用户月发送上限。
  • 冷却时间。
  • 奖励门槛和奖励名称。
  • 文案预览。

后台短信记录页可以作为二期,第一版可先通过数据库和日志排查。

测试范围

  • 租号订单完成后创建短信待发送记录。
  • 撞车订单完成不创建短信。
  • 本月完成订单数统计正确。
  • 根据奖励门槛计算 remaining_count 正确。
  • 找不到下一奖励时不发送。
  • 单用户月上限生效。
  • 冷却时间生效。
  • 同一订单不会重复创建短信任务。
  • 短信发送失败不影响订单完成。