Files
hi-server/doc/app-invite-and-subscribe-discount-requirements-zh.md
shanshanzhong147 3e265bd837
Build docker and publish / build (20.15.1) (push) Failing after 13m47s
Build docker and publish / build (20.15.1) (pull_request) Failing after 15m7s
新功能: 还原 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 数据库连接配置
- 补齐相关需求与设计文档
2026-05-28 20:46:37 -07:00

12 KiB

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
  • 只查“当前用户邀请的记录”

请求参数建议

{
  "page": 1,
  "size": 10
}

返回字段建议

{
  "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

推荐结构如下:

{
  "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