新功能: 还原 v1/public/user/invite_sales 接口 + promo schema 修复迁移
- 还原 /v1/public/user/invite_sales 接口及 /invite/sales 别名(与 invite_records 并存) 逻辑/handler/types 与197fed7d删除前版本完全一致,按fefbd4f5新 routes 结构注册 - 新增迁移 02155_promo_schema_fix: 幂等修复 subscribe_promo / order 列类型与索引偏差 - 同步 etc/ppanel.yaml 数据库连接配置 - 补齐相关需求与设计文档
This commit is contained in:
@@ -0,0 +1,559 @@
|
||||
# App 邀请列表与商品套餐折扣需求梳理
|
||||
|
||||
本文基于当前 `ppanel-server` 代码现状,对以下两个需求做整理:
|
||||
|
||||
1. App 需要一个“邀请列表/邀请记录”接口。
|
||||
2. 商品套餐需要补充“折扣信息”,当存在折扣时,App 需要做样式展示,并支持用户继续下单购买。
|
||||
|
||||
目标是帮助产品、前端、后端快速统一口径,明确:
|
||||
|
||||
- 现在已有哪些接口可以复用
|
||||
- 哪些地方确实需要新增
|
||||
- “接口增加三个字段”更适合加在哪一层
|
||||
|
||||
---
|
||||
|
||||
## 1. 需求结论
|
||||
|
||||
### 1.1 邀请列表
|
||||
|
||||
当前公开侧已经有一部分邀请能力,但**没有一个完全匹配“App 邀请记录列表”语义的公开接口**。
|
||||
|
||||
现状:
|
||||
|
||||
- 已有 `GET /v1/public/user/affiliate/list`
|
||||
- 能返回“我邀请了哪些用户”
|
||||
- 但字段较少,只包含基础信息
|
||||
- 已有 `GET /v1/public/user/invite_sales`
|
||||
- 返回的是“被邀请用户的成交订单记录”
|
||||
- 不是“邀请用户记录列表”
|
||||
- 已有 `GET /v1/public/user/invite_stats`
|
||||
- 返回邀请统计
|
||||
- 不是列表
|
||||
|
||||
结论:
|
||||
|
||||
- 如果 App 只是要展示“我邀请了哪些人”,`/affiliate/list` 可以复用。
|
||||
- 如果 App 需要展示“邀请时间、是否购买、购买次数、带来的佣金/赠送天数”等完整邀请记录,则**建议新增一个公开接口**。
|
||||
|
||||
### 1.2 商品套餐折扣
|
||||
|
||||
当前公开侧套餐列表接口 `GET /v1/public/subscribe/list` **已经返回 `discount` 字段**,下单预览接口 `POST /v1/public/order/pre` 也已经支持折扣计算。
|
||||
|
||||
现状:
|
||||
|
||||
- 套餐列表已有折扣规则数组 `discount`
|
||||
- 预下单接口已有:
|
||||
- 原价 `price`
|
||||
- 实付 `amount`
|
||||
- 折扣金额 `discount`
|
||||
- 活动优惠 `promo_discount`
|
||||
- 优惠券减免 `coupon_discount`
|
||||
- 手续费 `fee_amount`
|
||||
|
||||
结论:
|
||||
|
||||
- 后端**不是完全没有折扣能力**,而是“已经有计算能力,但 App 展示层使用起来不够直接”。
|
||||
- 如果需求明确要求“接口增加三个字段”,**更推荐补在套餐列表返回的 `discount[]` 子项里**,而不是直接加在下单接口里。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前代码现状
|
||||
|
||||
## 2.1 邀请相关
|
||||
|
||||
### 2.1.1 已有公开接口
|
||||
|
||||
#### A. 邀请基础列表
|
||||
|
||||
接口:
|
||||
|
||||
- `GET /v1/public/user/affiliate/list`
|
||||
|
||||
请求:
|
||||
|
||||
- `page`
|
||||
- `size`
|
||||
|
||||
返回结构:
|
||||
|
||||
- `total`
|
||||
- `list[]`
|
||||
- `identifier`
|
||||
- `avatar`
|
||||
- `registered_at`
|
||||
- `enable`
|
||||
|
||||
特点:
|
||||
|
||||
- 能表达“我邀请了谁”
|
||||
- 不能表达“是否购买 / 购买次数 / 给我带来多少收益”
|
||||
|
||||
对应代码:
|
||||
|
||||
- `apis/public/user.api`
|
||||
- `internal/logic/public/user/queryUserAffiliateListLogic.go`
|
||||
|
||||
#### B. 邀请成交记录
|
||||
|
||||
接口:
|
||||
|
||||
- `GET /v1/public/user/invite_sales`
|
||||
|
||||
返回结构:
|
||||
|
||||
- `total`
|
||||
- `list[]`
|
||||
- `amount`
|
||||
- `updated_at`
|
||||
- `user_hash`
|
||||
- `product_name`
|
||||
|
||||
特点:
|
||||
|
||||
- 更像“邀请带来的订单流水”
|
||||
- 不是邀请用户列表
|
||||
|
||||
对应代码:
|
||||
|
||||
- `internal/logic/public/user/getInviteSalesLogic.go`
|
||||
|
||||
#### C. 邀请统计
|
||||
|
||||
接口:
|
||||
|
||||
- `GET /v1/public/user/invite_stats`
|
||||
|
||||
返回结构:
|
||||
|
||||
- `friendly_count`
|
||||
- `history_count`
|
||||
|
||||
特点:
|
||||
|
||||
- 只适合头部统计卡片
|
||||
- 不适合列表页
|
||||
|
||||
对应代码:
|
||||
|
||||
- `internal/logic/public/user/getUserInviteStatsLogic.go`
|
||||
|
||||
### 2.1.2 已有后台接口
|
||||
|
||||
后台已经有更完整的邀请记录能力,可以直接参考:
|
||||
|
||||
- `GetAdminUserInviteList`
|
||||
- `GetInviteManageList`
|
||||
|
||||
这些接口已经能返回:
|
||||
|
||||
- 邀请时间
|
||||
- 是否购买
|
||||
- 购买次数
|
||||
- 邀请人佣金
|
||||
- 邀请人赠送天数
|
||||
- 被邀请人赠送天数
|
||||
|
||||
对应代码:
|
||||
|
||||
- `internal/logic/admin/user/getAdminUserInviteListLogic.go`
|
||||
- `internal/logic/admin/invite/getInviteManageListLogic.go`
|
||||
|
||||
结论:
|
||||
|
||||
- 邀请记录的统计逻辑后端已经有现成实现思路。
|
||||
- 新增 App 公开接口时,建议复用这部分逻辑,不要从零再写一套。
|
||||
|
||||
---
|
||||
|
||||
## 2.2 套餐折扣相关
|
||||
|
||||
### 2.2.1 套餐列表接口已有折扣规则
|
||||
|
||||
接口:
|
||||
|
||||
- `GET /v1/public/subscribe/list`
|
||||
|
||||
当前返回的套餐结构 `Subscribe` 中已包含:
|
||||
|
||||
- `unit_price`
|
||||
- `discount []SubscribeDiscount`
|
||||
|
||||
其中 `SubscribeDiscount` 当前字段为:
|
||||
|
||||
- `quantity`
|
||||
- `discount`
|
||||
- `new_user_only`
|
||||
- `map_apple`
|
||||
- `promo`
|
||||
|
||||
对应代码:
|
||||
|
||||
- `apis/public/subscribe.api`
|
||||
- `internal/logic/public/subscribe/querySubscribeListLogic.go`
|
||||
- `internal/types/types.go`
|
||||
|
||||
说明:
|
||||
|
||||
- `discount` 是按购买数量 `quantity` 生效的阶梯折扣
|
||||
- 不是单个套餐固定只有一个折扣值
|
||||
|
||||
### 2.2.2 预下单接口已有价格计算结果
|
||||
|
||||
接口:
|
||||
|
||||
- `POST /v1/public/order/pre`
|
||||
|
||||
当前已返回:
|
||||
|
||||
- `price`:原价
|
||||
- `amount`:最终应付
|
||||
- `discount`:折扣减免金额
|
||||
- `promo_discount`:活动优惠金额
|
||||
- `gift_amount`:礼品余额抵扣
|
||||
- `coupon_discount`:优惠券减免
|
||||
- `fee_amount`:手续费
|
||||
|
||||
对应代码:
|
||||
|
||||
- `apis/public/order.api`
|
||||
- `internal/logic/public/order/preCreateOrderLogic.go`
|
||||
|
||||
说明:
|
||||
|
||||
- 只要 App 知道 `subscribe_id + quantity [+ coupon] [+ payment]`,就已经能拿到准确的下单金额
|
||||
- 所以“下单购买”这件事本身,后端主链路已经具备
|
||||
|
||||
---
|
||||
|
||||
## 3. 差距分析
|
||||
|
||||
## 3.1 邀请列表的真实缺口
|
||||
|
||||
如果产品要的是“邀请记录页”,通常至少会关心以下内容:
|
||||
|
||||
- 被邀请用户
|
||||
- 邀请时间
|
||||
- 是否已购买
|
||||
- 购买次数
|
||||
- 给邀请人带来的佣金
|
||||
- 双方赠送天数
|
||||
|
||||
而当前:
|
||||
|
||||
- `/affiliate/list` 只有基础用户列表
|
||||
- `/invite_sales` 是订单成交记录
|
||||
- `/invite_stats` 是统计值
|
||||
|
||||
所以当前公开侧缺一个“**邀请关系维度的邀请记录列表**”。
|
||||
|
||||
## 3.2 套餐折扣的真实缺口
|
||||
|
||||
后端目前的主要问题不是“不会算折扣”,而是:
|
||||
|
||||
- `discount[]` 更偏规则定义
|
||||
- App 如果只想直接展示“折后价 / 优惠金额 / 折扣标签”,还需要自己再算一层
|
||||
- 这会增加前端理解成本,也容易和后端口径不一致
|
||||
|
||||
所以当前更合理的改法是:
|
||||
|
||||
- 保留现有折扣规则
|
||||
- 再额外补充几个**面向展示的字段**
|
||||
|
||||
---
|
||||
|
||||
## 4. 推荐方案
|
||||
|
||||
## 4.1 邀请记录接口
|
||||
|
||||
### 方案建议
|
||||
|
||||
新增一个公开接口,例如:
|
||||
|
||||
- `GET /v1/public/user/invite_records`
|
||||
|
||||
说明:
|
||||
|
||||
- 从登录态中取当前用户 ID
|
||||
- 不从前端传 `user_id`
|
||||
- 只查“当前用户邀请的记录”
|
||||
|
||||
### 请求参数建议
|
||||
|
||||
```json
|
||||
{
|
||||
"page": 1,
|
||||
"size": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 返回字段建议
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 2,
|
||||
"list": [
|
||||
{
|
||||
"invitee_id": 1001,
|
||||
"invitee_identifier": "138****8888",
|
||||
"invitee_avatar": "https://...",
|
||||
"invitee_enable": true,
|
||||
"invited_at": 1716800000,
|
||||
"order_count": 3,
|
||||
"has_purchased": true,
|
||||
"inviter_commission": 1200,
|
||||
"inviter_gift_days": 30,
|
||||
"invitee_gift_days": 7
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
|
||||
- `invitee_id`:被邀请用户 ID
|
||||
- `invitee_identifier`:被邀请用户展示账号
|
||||
- `invitee_avatar`:头像
|
||||
- `invitee_enable`:是否启用
|
||||
- `invited_at`:邀请时间
|
||||
- `order_count`:该被邀请用户产生的有效订单数
|
||||
- `has_purchased`:是否已购买
|
||||
- `inviter_commission`:给邀请人带来的佣金,单位建议继续沿用分
|
||||
- `inviter_gift_days`:邀请人获赠天数
|
||||
- `invitee_gift_days`:被邀请人获赠天数
|
||||
|
||||
### 实现建议
|
||||
|
||||
优先复用现有后台逻辑思路:
|
||||
|
||||
- 参考 `internal/logic/admin/user/getAdminUserInviteListLogic.go`
|
||||
- 或参考 `internal/logic/admin/invite/getInviteManageListLogic.go`
|
||||
|
||||
公开接口与后台接口的主要差异只有两点:
|
||||
|
||||
- 公开接口不允许前端指定 `user_id`
|
||||
- 公开接口按当前登录用户本人维度返回
|
||||
|
||||
### 是否可以不新增接口
|
||||
|
||||
可以,但前提是 App 接受以下拆分:
|
||||
|
||||
- 列表页用 `/affiliate/list`
|
||||
- 顶部统计用 `/invite_stats`
|
||||
- 订单流水页用 `/invite_sales`
|
||||
|
||||
如果产品要的是一个完整“邀请记录页”,不建议这样拆三次请求,前端维护成本偏高。
|
||||
|
||||
---
|
||||
|
||||
## 4.2 套餐折扣字段建议
|
||||
|
||||
### 核心建议
|
||||
|
||||
“接口增加三个字段”建议**加在 `SubscribeDiscount` 子项里**,不要直接加在 `Subscribe` 顶层。
|
||||
|
||||
原因:
|
||||
|
||||
- 折扣是按 `quantity` 生效的
|
||||
- 一个套餐可能有多个折扣档位
|
||||
- 如果加在套餐顶层,很难表达“买 1 个月”和“买 12 个月”对应不同折扣
|
||||
|
||||
### 推荐新增字段
|
||||
|
||||
建议在 `SubscribeDiscount` 中增加以下三个展示字段:
|
||||
|
||||
- `discount_price`
|
||||
- `discount_amount`
|
||||
- `discount_desc`
|
||||
|
||||
推荐结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"quantity": 12,
|
||||
"discount": 80,
|
||||
"new_user_only": false,
|
||||
"map_apple": "",
|
||||
"promo": null,
|
||||
"discount_price": 9600,
|
||||
"discount_amount": 2400,
|
||||
"discount_desc": "年付8折"
|
||||
}
|
||||
```
|
||||
|
||||
### 三个字段的含义
|
||||
|
||||
#### 1. `discount_price`
|
||||
|
||||
- 含义:该档位折后总价
|
||||
- 计算建议:`unit_price * quantity * discount / 100`
|
||||
- 单位:分
|
||||
|
||||
作用:
|
||||
|
||||
- App 可直接展示“折后价”
|
||||
- 下单时直接把该项的 `quantity` 带入 `/order/pre` 或 `/order/purchase`
|
||||
|
||||
#### 2. `discount_amount`
|
||||
|
||||
- 含义:该档位比原价便宜多少钱
|
||||
- 计算建议:`unit_price * quantity - discount_price`
|
||||
- 单位:分
|
||||
|
||||
作用:
|
||||
|
||||
- App 可直接展示“立省 xx”
|
||||
|
||||
#### 3. `discount_desc`
|
||||
|
||||
- 含义:折扣展示文案
|
||||
- 示例:
|
||||
- `年付8折`
|
||||
- `季付9折`
|
||||
- `新用户首单8折`
|
||||
|
||||
作用:
|
||||
|
||||
- App 可直接做角标、标签、促销文案展示
|
||||
|
||||
### 为什么不推荐这三个字段加在下单接口
|
||||
|
||||
因为下单接口本来就是“结果型接口”,它已经能返回:
|
||||
|
||||
- 原价
|
||||
- 折扣金额
|
||||
- 实付金额
|
||||
|
||||
如果只是为了 App 卡片展示,再去每个套餐都调一次 `/order/pre`,成本会比较高:
|
||||
|
||||
- 请求次数多
|
||||
- 页面首屏会更慢
|
||||
- 前端链路更复杂
|
||||
|
||||
更合适的做法是:
|
||||
|
||||
- 套餐列表接口负责“展示友好”
|
||||
- 预下单接口负责“结算准确”
|
||||
|
||||
---
|
||||
|
||||
## 5. 推荐改动清单
|
||||
|
||||
## 5.1 邀请列表
|
||||
|
||||
建议新增:
|
||||
|
||||
- 新接口:`GET /v1/public/user/invite_records`
|
||||
|
||||
建议新增类型:
|
||||
|
||||
- `GetUserInviteRecordsRequest`
|
||||
- `UserInviteRecord`
|
||||
- `GetUserInviteRecordsResponse`
|
||||
|
||||
建议实现位置:
|
||||
|
||||
- `apis/public/user.api`
|
||||
- `internal/types/types.go`
|
||||
- `internal/handler/public/user/`
|
||||
- `internal/logic/public/user/`
|
||||
|
||||
## 5.2 套餐折扣
|
||||
|
||||
建议调整:
|
||||
|
||||
- `SubscribeDiscount` 增加 3 个字段:
|
||||
- `discount_price`
|
||||
- `discount_amount`
|
||||
- `discount_desc`
|
||||
|
||||
建议实现位置:
|
||||
|
||||
- `apis/types.api`
|
||||
- `internal/types/types.go`
|
||||
- `internal/logic/public/subscribe/querySubscribeListLogic.go`
|
||||
|
||||
---
|
||||
|
||||
## 6. 前后端协作建议
|
||||
|
||||
## 6.1 App 侧调用建议
|
||||
|
||||
邀请页建议:
|
||||
|
||||
- 头部统计:`/v1/public/user/invite_stats`
|
||||
- 邀请记录列表:`/v1/public/user/invite_records`
|
||||
- 如果还要看成交流水:`/v1/public/user/invite_sales`
|
||||
|
||||
套餐页建议:
|
||||
|
||||
- 先调 `/v1/public/subscribe/list` 渲染套餐和折扣标签
|
||||
- 用户点某个折扣档位时,带 `subscribe_id + quantity` 调 `/v1/public/order/pre`
|
||||
- 用户确认后再调 `/v1/public/order/purchase`
|
||||
|
||||
## 6.2 单位口径建议
|
||||
|
||||
建议继续保持后端金额统一为“分”:
|
||||
|
||||
- `unit_price`
|
||||
- `discount_price`
|
||||
- `discount_amount`
|
||||
- `amount`
|
||||
- `coupon_discount`
|
||||
|
||||
这样可以避免前后端出现小数精度问题。
|
||||
|
||||
---
|
||||
|
||||
## 7. 最终建议
|
||||
|
||||
### 建议一
|
||||
|
||||
如果你们只是要“邀请用户名单”,可直接复用:
|
||||
|
||||
- `GET /v1/public/user/affiliate/list`
|
||||
|
||||
### 建议二
|
||||
|
||||
如果你们要的是完整“邀请记录”,建议新增:
|
||||
|
||||
- `GET /v1/public/user/invite_records`
|
||||
|
||||
这是本次需求里更合理的新增接口。
|
||||
|
||||
### 建议三
|
||||
|
||||
商品套餐“折扣信息”不建议重新设计一整套下单逻辑。
|
||||
|
||||
当前后端已经具备:
|
||||
|
||||
- 套餐折扣规则
|
||||
- 预下单价格计算
|
||||
- 正式下单购买
|
||||
|
||||
更推荐做法是:
|
||||
|
||||
- 在 `SubscribeDiscount` 里补 3 个展示字段:
|
||||
- `discount_price`
|
||||
- `discount_amount`
|
||||
- `discount_desc`
|
||||
|
||||
这样改动最小,也最贴近 App 展示场景。
|
||||
|
||||
---
|
||||
|
||||
## 8. 相关代码位置
|
||||
|
||||
- `internal/logic/public/user/queryUserAffiliateListLogic.go`
|
||||
- `internal/logic/public/user/getInviteSalesLogic.go`
|
||||
- `internal/logic/public/user/getUserInviteStatsLogic.go`
|
||||
- `internal/logic/admin/user/getAdminUserInviteListLogic.go`
|
||||
- `internal/logic/admin/invite/getInviteManageListLogic.go`
|
||||
- `internal/logic/public/subscribe/querySubscribeListLogic.go`
|
||||
- `internal/logic/public/order/preCreateOrderLogic.go`
|
||||
- `internal/logic/public/order/purchaseLogic.go`
|
||||
- `internal/types/types.go`
|
||||
- `apis/public/user.api`
|
||||
- `apis/public/subscribe.api`
|
||||
- `apis/public/order.api`
|
||||
|
||||
Reference in New Issue
Block a user