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

343 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 租客成长等级与主动短信触达方案
## 一、租客成长等级
### 功能范围
- 只统计租号订单,即 `rental_orders`
- 不统计撞车订单,即 `mohong_orders`
- 积分只按已完成租号订单的实付租金累计,不含押金。
- 等级三档:铂金、钻石、巅峰。
- 下单租号时按用户当前等级使用折扣。
- 折扣和升级门槛后台可配置。
- 历史订单保存价格、等级、折扣快照,后台修改配置不影响历史订单。
### 积分规则
推荐规则:
```text
成长积分 = 已完成租号订单实付租金金额
1 元实付租金 = 1 积分
```
不建议统计押金,原因是押金可能退款、免押、暂扣或赔付,直接计入成长积分会让财务口径变复杂。
积分发放时机建议放在租号订单完成后,而不是支付成功后。这样可以避免用户付款后退款、取消或关闭订单导致刷积分。
### 后台配置
使用现有 `system_configs`,新增配置项:
```text
renter.growth_level_rules
```
建议默认值:
```json
{
"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`:本单已发放积分,用于防重复。
### 下单折扣
在订单创建阶段应用折扣,支付阶段不重新计算。
现有租号订单创建位置:
```text
backend/internal/modules/order/lifecycle.go
```
现有支付金额来自:
```text
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`
默认只升级不降级。若后台未来调整门槛,可以选择只影响后续升级判断,不主动批量降级。
### 前端范围
后台系统配置页:
- 配置是否启用。
- 配置每元积分。
- 配置三个等级的门槛积分。
- 配置三个等级的折扣。
后台用户管理:
- 展示成长等级。
- 展示成长积分。
- 支持后台调整积分或等级,操作写审计日志。
用户端订单页:
- 展示租金原价。
- 展示等级折扣。
- 展示优惠金额。
- 展示折后租金。
- 展示押金。
- 展示应付合计。
### 测试范围
- 铂金、钻石、巅峰不同等级折扣计算正确。
- 押金不打折。
- 折扣不影响号主收入。
- 支付金额等于折后租金加实付押金。
- 撞车订单完成不增加成长积分。
- 租号订单完成只发放一次成长积分。
- 积分达到门槛后自动升级。
- 后台修改配置不影响历史订单快照。
## 二、主动短信触达
### 业务目标
用户完成一笔租号订单后,平台可以主动发送短信,告知用户本月已经累计完成多少笔租号订单,以及距离领取奖励还差多少笔。
示例文案:
```text
尊敬的 用户名:
您好,您已经在 大锤商行 累计完成 1 笔租号订单,再完成 x 笔即可领取 xxxx。
```
### 统计口径
- 只统计 `rental_orders`
- 只统计状态为 `completed` 的租号订单。
- 不统计 `mohong_orders` 撞车订单。
- 按自然月统计,建议使用订单完成时间 `settled_at`
- 月统计区间为当前月第一天 00:00:00 到下月第一天 00:00:00。
统计 SQL 口径示例:
```sql
SELECT COUNT(*)
FROM rental_orders
WHERE renter_id = ?
AND status = 'completed'
AND settled_at >= ?
AND settled_at < ?;
```
### 触发时机
推荐触发点:租号订单完成后。
现有完成入口:
```text
backend/internal/modules/order/checkout_finalize.go
```
`finalizeCheckout` 会把订单状态改为 `completed`。短信不建议在事务内直接发送,避免短信服务失败导致订单完成失败。
推荐流程:
1. 订单完成事务内写入短信待发送记录。
2. 事务提交后由后台任务发送短信。
3. 发送成功、失败、重试次数都落库。
### 短信通道
项目已有短信集成:
```text
backend/internal/integrations/sms
```
当前 `Provider` 只支持登录验证码:
```go
SendLoginCode(ctx context.Context, phone string, code string) error
```
主动短信需要扩展为模板短信能力,例如:
```go
SendTemplate(ctx context.Context, phone string, templateCode string, params map[string]string) error
```
阿里云短信实际发送不能随意拼接任意文案,必须使用已审核的短信模板。因此后台可配置“模板 code、奖励规则、预览文案”,真实发送时只传模板变量。
### 后台配置
使用 `system_configs`,新增配置项:
```text
sms.rental_completion_reminder
```
建议默认值:
```json
{
"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. 如果没有下一个奖励,默认不发送,或后续配置为发送达成提醒。
### 数据库表
建议新增短信发送记录表:
```text
sms_messages
```
字段建议:
- `id`
- `user_id`
- `phone`
- `biz_type`:例如 `rental_completion_reminder`
- `biz_id`:租号订单 ID。
- `template_code`
- `template_params`
- `content_preview`
- `status``pending``sent``failed``cancelled`
- `retry_count`
- `last_error`
- `sent_at`
- `created_at`
- `updated_at`
建议加唯一索引:
```text
uk_sms_messages_biz_type_biz_id_user
```
用于保证同一笔完成订单不会重复创建短信任务。
### 发送任务
新增后台任务,例如:
```text
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` 正确。
- 找不到下一奖励时不发送。
- 单用户月上限生效。
- 冷却时间生效。
- 同一订单不会重复创建短信任务。
- 短信发送失败不影响订单完成。