txagent-y/docs/dev/api/API-05-订阅系统.md

19 KiB
Raw Blame History

API-05 订阅系统SUBSCRIPTION

共 9 个接口SUB-01~09

订阅档位枚举(含免费关注,最多 5 档;付费档与内容可见性 visibility 1-4 对齐)

tier_level API 字段值 说明
0 free 免费关注(粉丝无需付费即可订阅)
1 junior 初级会员
2 basic 基础会员
3 senior 高级会员
4 supreme 至尊会员

创作者可按需启用 1-4 档付费档必须连续免费关注层tier_level=0始终存在且 price 必须为 0。免费档与付费档互斥任一付费档启用时免费档自动关闭全部付费档关闭时免费档自动开启。同一创作者订阅档位含免费层最多 5 行,付费档最多 4 个。 内容可见性 visibility=0(免费)/1(初级)/2(基础)/3(高级)/4(至尊) 对应订阅 tier_levelvisibility=0 无需付费订阅,但仍需关注/订阅免费层)。

内容权限枚举subscription_tier.permissions

说明
post 动态(图文帖子)
video 专属视频内容
dm 1对1私信聊天
ppv_discount PPV 内容享折扣
live 专属直播间
custom 专属定制素材

每个档位可多选 0~6 项;前端编辑层级弹框对应该字段。

钱包扣款乐观锁规范(适用于 SUB-02/03/05

所有从钱包扣款的写入必须按以下方式执行(避免并发超扣):

-- 1. 先读取 wallet取 balance + version
SELECT balance, version FROM wallet WHERE user_id = :uid;

-- 2. 单条 UPDATE受影响行数 = 0 时返回 409 由前端重试
UPDATE wallet
   SET balance = balance - :amount,
       total_expense = total_expense + :amount,
       version = version + 1,
       updated_at = NOW()
 WHERE user_id = :uid
   AND version = :old_version
   AND balance >= :amount;

写入 wallet_transaction 流水必须与上述 UPDATE 在同一事务内。

时间戳字段命名规范

API 响应中的时间戳字段统一使用 At 后缀:

字段 说明
startAt 订阅开始时间
expireAt 订阅到期时间
createdAt 创建时间

对应 DB 字段使用 _at 后缀started_at、expired_at、created_at

订阅状态流转

无订阅 ──SUB-02──→ active生效中
                      │
                SUB-03 升级 → 更高档位(即时生效)
                SUB-04 降级 → 标记 next_tier_level下周期生效
                SUB-08 取消 → 标记 auto_renew=0到期后不续费
                      │
                  到期 → grace_period3天宽限期
                      │
               SUB-05 续费 → active
                      │
                宽限期结束 → expired

SUB-01 获取创作者订阅档位

项目 说明
接口路径 GET /client/api/v1/subscription/tiers/:creatorId
接口用途 获取某创作者的订阅档位信息
权限要求 公开(游客可看价格)
涉及表 subscription_tier, user_subscription

接口逻辑:

  1. 查询 subscription_tier 表 WHERE creator_id=creatorId AND is_active=1 ORDER BY tier_level ASC仅返回当前启用档位若开启任意付费档则不会返回 free
  2. 如果用户已登录,查询 user_subscription 表 WHERE user_id=me AND creator_id=creatorId
  3. 组装当前订阅信息currentSubscription未订阅时为 null若用户仍处于一个已关闭档位的有效周期内仍需回填该档位名称
  4. 返回档位列表 + 当前订阅状态

路径参数:

字段 类型 必选 说明
creatorId string 创作者ID

响应数据:

字段 类型 说明
tiers array 订阅档位列表(仅已开启的)
tiers[].tierId string 档位ID
tiers[].tierLevel number 档位等级0=免费关注 / 1=初级 / 2=基础 / 3=高级 / 4=至尊
tiers[].name string 档位名称
tiers[].price number 月订阅价格(糖心币)
tiers[].description string 档位权益说明
tiers[].permissions string[] 内容权限多选(来自 subscription_tier.permissions,枚举见顶部「内容权限枚举」)
tiers[].permissionLabels string[] 权限展示文案(按 permissions 顺序映射的中文标签)
currentSubscription object/null 当前订阅信息
currentSubscription.subscriptionId string 订阅ID
currentSubscription.tierLevel number 当前档位等级
currentSubscription.tierName string 当前档位名称
currentSubscription.expireAt timestamp 到期时间
currentSubscription.isActive boolean 是否有效
currentSubscription.autoRenew boolean 是否自动续费

SUB-02 购买订阅

项目 说明
接口路径 POST /client/api/v1/subscription/purchase
接口用途 用户订阅某创作者的某个档位
权限要求 需登录
涉及表 subscription_tier, subscription_order, user_subscription, wallet, wallet_transaction, revenue_share_record, follow

接口逻辑:

  1. 查询 subscription_tier 表 根据 tierId 获取档位信息和价格(读)
  2. 幂等校验:查询 user_subscription 表 检查是否已订阅同档位WHERE user_id=me AND creator_id AND status=1
  3. 查询 wallet 表 检查 balance ≥ 档位价格(读)→ 不足返回422
  4. 扣款:更新 wallet 表 的 balance -= price原子操作更新
  5. 创作者收入先按全额入钱包:更新创作者 wallet 表的 balance/frozen_balance、total_income += 全额金额(更新)
  6. 写入 subscription_orderorder_type=1新订阅amount=pricesubscription_tier_id=tierId
  7. 写入/更新 user_subscriptionstatus=1生效中设置 started_at、expired_at=+30天
  8. 写入 wallet_transaction 表(用户端 type=2订阅扣款 + 创作者端 type=5订阅收入创作者端设置 frozen_until=当前时间+7天
  9. 写入 revenue_share_recordorder_id=subscription_order._id, order_type=1, 记录三方分润明细 + 收入口径标记, settlement_status=1冻结中, available_at=当前时间+7天
  10. 自动关注:查询 follow 表 是否已关注,未关注则写入(写 follow
  11. 更新 creator_profile 表 的 subscriber_count+1更新
  12. 返回订阅信息

关注/取关对称性说明: 订阅时自动关注(单向),但取消订阅/到期后不自动取关。关注是独立行为(免费),用户可通过 CREATOR-05 手动取关。

请求参数Body

字段 类型 必选 说明
creatorId string 创作者ID
tierId string 订阅档位ID

响应数据:

字段 类型 说明
subscriptionId string 订阅ID
creatorId string 创作者ID
tierId string 档位ID
tierName string 档位名称
price number 扣除的糖心币
startAt timestamp 订阅开始时间
expireAt timestamp 订阅到期时间
remainingBalance number 扣费后余额

SUB-03 升级订阅

项目 说明
接口路径 POST /client/api/v1/subscription/:subscriptionId/upgrade
接口用途 从低档升到高档(即时生效,补差价)
权限要求 需登录
涉及表 user_subscription, subscription_tier, subscription_order, wallet, wallet_transaction

接口逻辑:

  1. 查询 user_subscription 表 根据 subscriptionId 获取当前订阅(读)
  2. 校验 user_subscription.user_id = 当前用户
  3. 查询 subscription_tier 表 获取当前档位和目标档位的价格(读)
  4. 校验目标档位 > 当前档位(否则走降级)
  5. 计算补差价 = (高档日单价 - 低档日单价) × 剩余天数(向上取整)
  6. 查询 wallet 表 检查 balance ≥ 补差价(读)→ 不足返回422
  7. 扣款:更新 wallet 表 的 balance -= 补差价(原子操作,更新)
  8. 创作者收入先按全额入钱包,同时保留分润明细用于提现结算
  9. 更新 user_subscription 表 的 tier_level = 目标档位(更新)
  10. 写入 subscription_orderorder_type=2升级amount=补差价)(写)
  11. 写入 wallet_transaction 表(写)
  12. 返回升级结果

路径参数:

字段 类型 必选 说明
subscriptionId string 当前订阅ID

请求参数Body

字段 类型 必选 说明
targetTierId string 目标高档位ID

响应数据:

字段 类型 说明
subscriptionId string 订阅ID
oldTierId string 原档位ID
newTierId string 新档位ID
oldTierLevel number 原档位等级
newTierLevel number 新档位等级
priceDiff number 补差价金额
newExpireAt timestamp 到期时间(不变)
remainingBalance number 扣费后余额

SUB-04 降级订阅

项目 说明
接口路径 POST /client/api/v1/subscription/:subscriptionId/downgrade
接口用途 设置下个周期降级到低档(当前周期不变)
权限要求 需登录
涉及表 user_subscription

接口逻辑:

  1. 查询 user_subscription 表 根据 subscriptionId 获取当前订阅(读)
  2. 校验 user_subscription.user_id = 当前用户
  3. 校验目标档位 < 当前档位
  4. 更新 user_subscription 表 的 next_tier_level = 目标档位等级(更新)
  5. 返回降级预告信息

路径参数:

字段 类型 必选 说明
subscriptionId string 当前订阅ID

请求参数Body

字段 类型 必选 说明
targetTierId string 目标低档位ID

响应数据:

字段 类型 说明
subscriptionId string 订阅ID
currentTierId string 当前档位ID
currentTierLevel number 当前档位等级
pendingDowngradeTierLevel number 下周期降级到的档位等级
pendingDowngradeTierId string 下周期降级到的档位ID
currentExpireAt timestamp 当前周期到期时间

SUB-05 手动续费

项目 说明
接口路径 POST /client/api/v1/subscription/:subscriptionId/renew
接口用途 订阅到期后用户手动续费
权限要求 需登录
涉及表 user_subscription, subscription_tier, subscription_order, wallet, wallet_transaction

接口逻辑:

  1. 查询 user_subscription 表 根据 subscriptionId 获取订阅(读)
  2. 确定续费档位:如果 next_tier_level 不为 null → 按降级后的档位续费,否则按当前 tier_level
  3. 查询 subscription_tier 表 获取续费价格(读)
  4. 校验 subscription_tier.is_active=1创作者未关闭该档位→ 关闭则返回422
  5. 查询 wallet 表 检查 balance ≥ 续费价格(读)
  6. 扣款:更新 wallet 表(原子操作,更新)
  7. 创作者收入先按全额入钱包,同时保留分润明细用于提现结算
  8. 更新 user_subscriptiontier_level=续费档位status=1expired_at=+30天next_tier_level=null更新
  9. 写入 subscription_orderorder_type=4续费
  10. 写入 wallet_transaction 表(写)
  11. 返回续费结果

路径参数:

字段 类型 必选 说明
subscriptionId string 订阅ID

响应数据:

字段 类型 说明
subscriptionId string 订阅ID
tierId string 续费档位ID
tierLevel number 续费档位等级
tierName string 续费档位名称
price number 扣除的糖心币
newExpireAt timestamp 新的到期时间
remainingBalance number 扣费后余额

SUB-06 查询我的订阅列表

项目 说明
接口路径 GET /client/api/v1/subscription/mine
接口用途 查看用户当前订阅的创作者列表
权限要求 需登录
涉及表 user_subscription, user, subscription_tier

接口逻辑:

  1. 查询 user_subscription 表 WHERE user_id=当前用户,可选按 status 筛选(读)
  2. 关联查询 user 表 获取创作者信息profile.display_name、profile.avatar_url
  3. 关联查询 subscription_tier 表 获取档位名称和价格(读)
  4. 分页返回

请求参数Query

字段 类型 必选 说明
status string 状态active/expired
page number 页码
pageSize number 每页条数

响应数据:

字段 类型 说明
list array 订阅列表
list[].subscriptionId string 订阅ID
list[].creator object 创作者信息
list[].creator.userId string 创作者ID
list[].creator.nickname string 创作者昵称
list[].creator.avatar url 创作者头像
list[].tierId string 档位ID
list[].tierLevel number 档位等级
list[].tierName string 档位名称
list[].price number 订阅价格
list[].status string 状态active/expired/grace_period
list[].startAt timestamp 开始时间
list[].expireAt timestamp 到期时间
list[].autoRenew boolean 是否自动续费
list[].pendingDowngradeTierLevel number/null 下周期降级档位等级
pagination object 分页信息

SUB-07 设置订阅档位(创作者)

项目 说明
接口路径 PUT /client/api/v1/subscription/tiers/settings
接口用途 创作者设置/修改订阅档位(含免费关注 + 1~4 个付费档)的名称、价格、权限、开关
权限要求 创作者
涉及表 subscription_tier, user_subscription

接口逻辑:

  1. 校验当前用户为创作者
  2. 校验: - 必须包含 tier=freetier_level=0且 price=0 - 付费档tier_level 1-4数量 ≤ 4且启用的档位 tier_level 必须从 1 开始连续(不能跳级) - 免费档与付费档互斥:任一付费档启用时,后端自动关闭 free全部付费档关闭时后端自动开启 free - 每个档位 permissions 仅允许枚举值
  3. 遍历请求中的 tiers 数组:
    • 查询/更新 subscription_tierWHERE creator_id=me AND tier_level=对应等级)(读+更新)
    • 如果记录不存在则创建(首次设置时写入对应记录)(写)
  4. 更新 is_active、price、tier_name、description、permissions 字段
  5. 统计各档位订阅人数:查询 user_subscription 表 COUNT(WHERE creator_id=me AND tier_level=X AND status=1)(读)
  6. 返回更新后的档位列表

删除档位语义: 客户端「删除」按钮等同于将该档位 enabled=false软关闭is_active=0。已订阅该档位的用户保留至到期关闭后用户无法再购买、续费或降级到该档位。免费档不单独手工开关而是由后端按互斥规则自动开关。

请求参数Body

字段 类型 必选 说明
tiers array 档位设置数组(含 free 与 1~4 个付费档)
tiers[].tier string 档位标识free/junior/basic/senior/supreme
tiers[].enabled boolean 是否开启付费档按传值生效free 的最终开关状态由后端按互斥规则自动归一
tiers[].price number 月价格糖心币free=0付费档 >0
tiers[].name string 自定义档位名称(默认使用枚举名)
tiers[].description string 档位权益说明
tiers[].permissions string[] 内容权限多选枚举post/video/dm/ppv_discount/live/custom

响应数据:

字段 类型 说明
tiers array 更新后的档位列表
tiers[].tierId string 档位ID
tiers[].tierLevel number 档位等级
tiers[].name string 档位名称
tiers[].price number 价格
tiers[].description string 权益说明
tiers[].enabled boolean 是否开启
tiers[].permissions string[] 内容权限多选
tiers[].subscriberCount number 当前订阅人数

SUB-09 创作者查看自己的档位列表

项目 说明
接口路径 GET /client/api/v1/subscription/tiers/mine
接口用途 创作者进入「订阅设置」页时拉取自己的全部档位(含未启用),用于回填编辑表单
权限要求 创作者
涉及表 subscription_tier, user_subscription

接口逻辑:

  1. 校验当前用户为创作者role=2
  2. 查询 subscription_tier 表 WHERE creator_id=me含 is_active=0ORDER BY tier_level ASC
  3. 若免费层tier_level=0不存在默认补一个空记录返回不落库方便前端首次进入即可渲染其 enabled 状态应遵循当前互斥规则
  4. 对每个档位调用 TierSubscriberCount 统计当前订阅人数status=1
  5. 返回完整档位列表,每项包含 permissions/permissionLabels/subscriberCount

与 SUB-01 的区别SUB-01 仅返回 is_active=1 的档位粉丝视角SUB-09 返回创作者本人全部档位含未启用项(创作者视角,用于编辑回填)。

响应数据:

字段 类型 说明
tiers array 档位列表(含未启用)
tiers[].tierId string 档位ID默认 free 占位行为 0
tiers[].tierLevel number 档位等级 0/1/2/3/4
tiers[].tierKey string free/junior/basic/senior/supreme
tiers[].name string 档位名称
tiers[].price number 月价格
tiers[].description string 权益说明
tiers[].enabled boolean 是否启用
tiers[].permissions string[] 内容权限多选
tiers[].permissionLabels string[] 权限展示文案
tiers[].subscriberCount number 当前订阅人数

SUB-08 取消订阅

项目 说明
接口路径 POST /client/api/v1/subscription/:subscriptionId/cancel
接口用途 取消自动续费,当前周期仍有效,到期后不再续费
权限要求 需登录
涉及表 user_subscription

接口逻辑:

  1. 查询 user_subscription 表 根据 subscriptionId 获取订阅(读)
  2. 校验 user_subscription.user_id = 当前用户
  3. 校验 status=1生效中→ 非生效状态返回422
  4. 更新 user_subscription 表 的 auto_renew=0更新
  5. 当前周期权益不受影响,到期后自动转为 expired
  6. 返回取消结果

注:取消订阅不会立即失效,也不会退款。用户仍可在到期前通过 SUB-05 手动续费恢复。

路径参数:

字段 类型 必选 说明
subscriptionId string 订阅ID

响应数据:

字段 类型 说明
subscriptionId string 订阅ID
status string 状态active当前周期仍有效
autoRenew boolean 自动续费状态false
expireAt timestamp 到期时间(届时失效)