# 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`

