From e0cb15f408e3908079207ec1de91afdb05e3645e Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 25 Jul 2026 17:55:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=BD=AE=E6=92=AD=E9=81=AE?= =?UTF-8?q?=E7=BD=A9=E5=B9=B6=E8=A1=A5=E5=85=85=E6=96=B9=E6=A1=88=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/租客成长等级与主动短信触达方案.md | 342 ++++++++++++++++++ .../listings/components/HomeBanner.vue | 12 +- .../listings/views/MobileHomeView.css | 2 +- .../listings/views/MobileHomeView.vue | 10 +- 4 files changed, 362 insertions(+), 4 deletions(-) create mode 100644 docs/租客成长等级与主动短信触达方案.md diff --git a/docs/租客成长等级与主动短信触达方案.md b/docs/租客成长等级与主动短信触达方案.md new file mode 100644 index 0000000..d67633c --- /dev/null +++ b/docs/租客成长等级与主动短信触达方案.md @@ -0,0 +1,342 @@ +# 租客成长等级与主动短信触达方案 + +## 一、租客成长等级 + +### 功能范围 + +- 只统计租号订单,即 `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": 0, "discount_bps": 9800 }, + { "code": "diamond", "name": "钻石", "min_points": 1000, "discount_bps": 9500 }, + { "code": "peak", "name": "巅峰", "min_points": 5000, "discount_bps": 9000 } + ] +} +``` + +`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` 正确。 +- 找不到下一奖励时不发送。 +- 单用户月上限生效。 +- 冷却时间生效。 +- 同一订单不会重复创建短信任务。 +- 短信发送失败不影响订单完成。 diff --git a/frontend/src/features/listings/components/HomeBanner.vue b/frontend/src/features/listings/components/HomeBanner.vue index ee7871d..890e6c7 100644 --- a/frontend/src/features/listings/components/HomeBanner.vue +++ b/frontend/src/features/listings/components/HomeBanner.vue @@ -37,6 +37,10 @@ function openPreview(slide: HomeBannerSlide) { function closePreview() { previewVisible.value = false } + +function hasSlideCopy(slide: HomeBannerSlide) { + return Boolean(slide.eyebrow?.trim() || slide.title?.trim() || slide.pill?.trim()) +}