txagent-y/docs/dev/api/API-04-内容系统.md

25 KiB
Raw Blame History

API-04 内容系统CONTENT

共 12 个接口CONTENT-01~12

内容类型枚举post.type

DB值 API值 说明
1 post 动态图文1-9 张图)
2 video 视频(不区分时长)

PPV 素材不在此枚举,独立走 API-06 PPV 系统(数据模型完全不同)。发布页 UI 上"发布 PPV 素材"按钮直接跳转到 PPV 创建流程。

状态枚举映射post.status

DB值 API响应值 说明
0 draft 草稿(仅创作者本人可见)
1 reviewing 审核中
2 published 已上架
3 rejected 审核拒绝
4 removed 已下架

可见性与订阅等级对应关系free + 4 档)

visibility可见性 DB值 所需 subscription_tier.tier_level 说明
free 0 无需订阅 免费公开,所有用户可见
junior 1 ≥ 1 初级会员及以上
basic 2 ≥ 2 基础会员及以上
senior 3 ≥ 3 高级会员及以上
supreme 4 ≥ 4 至尊会员(独占)

判断逻辑: user_subscription.tier_level >= post.visibility 且订阅状态有效,满足即有权限。visibility=0 (free) 的内容无需检查订阅状态。

档位可配置说明: 系统固定 4 个 tier_level 槽位,但每个创作者可在 SUB-07 中独立启用 1~4 档(必须从 level=1 起连续启用),并自定义档位名称、描述、价格。因此创作者实际可选的 visibility 档位 = 该创作者已启用的 tier_level + free

App 发布表单约束CONTENT-01 发布页加载时应先调用 SUB-01 拉取当前创作者自己已启用的档位列表(GET /subscription/tiers/:creatorIdcreatorId 传自己),按返回的 tier_level 渲染"可见档位"下拉选项;不应再硬编码 5 项。后端会校验提交的 visibility 必须为 free 或当前创作者已启用且 is_active=true 的 tier_level 对应字符串。

若创作者尚未配置任何档位:仅允许选择 free;提交其它值返回 422。

关于评论commentCount

评论系统为 P1 扩展功能(参见数据库设计文档 post_comment 表)。当前阶段 commentCount 固定返回 0,评论 CRUD 接口将在 P1 阶段补充。


CONTENT-01 发布内容(含草稿)

项目 说明
接口路径 POST /client/api/v1/content
接口用途 创作者发布动态/视频,或保存为草稿
权限要求 创作者user.role=2
涉及表 post, post_media, post_tag, post_mention, post_highlight, media, tag

接口逻辑:

  1. 校验当前用户 user.role=2创作者权限
  2. 校验 mediaIds查询 media 表 确认 process_status=2 且 user_id=当前用户
  3. 校验 tags1-3 个):每项 idname 二选一。传 idtag 表存在性校验;只传 name 时若同名 tag 已存在则复用,否则当场创建 source=2 的自定义标签(等价于内联 CONTENT-12
  4. 校验 mentions查询 user 表 确认被 @ 用户存在
  5. 若 type=2video且包含 highlights校验 time_offset_seconds < 视频时长
  6. 校验 visibility若不是 free,查询 subscription_tier 表 WHERE creator_id=当前用户 AND tier_level=映射值 AND is_active=true不存在则返回 422"该档位未启用"
  7. 写入 poststatus=isDraft?0:1写入 location_name/codevisibility 存映射后的 int 值)
  8. 写入关系表post_media / post_tag / post_mention / post_highlight
  9. 草稿isDraft=true跳过审核流程非草稿进入审核队列
  10. 返回新创建的内容信息

请求参数Body

字段 类型 必选 说明
title string 标题,最长 30 字
content string 正文内容,最长 200 字
type string 内容类型post/video
mediaIds int64[] 媒体ID数组图片最多9张或1个视频
tags object[] 标签数组1-3 个;每项 idname 二选一
tags[].id string 已存在的标签 ID系统预设或已建好的自定义标签
tags[].name string 仅在不传 id 时使用同名复用否则新建自定义标签source=2长度 1~32
mentions string[] 被 @ 用户的 userId 数组,最多 10 个
location object 地点信息
location.name string 地点显示名(如"上海 静安区"),最长 64 字
location.code string 行政区划代码GB/T 2260最长 20 字
visibility string 可见性:free 或当前创作者已启用的档位 keyjunior/basic/senior/supreme 之一)。后端会校验该 key 对应的 tier_level 在当前创作者的 subscription_tier 中存在且 is_active=true,否则返回 422
highlights array 视频章节标记数组type=video 时),用于在播放进度条上打点跳转
highlights[].timeOffsetSeconds int 标记时间点(秒),必须 < 视频时长
highlights[].label string 标记说明文字(最长 40 字)
isDraft boolean 是否保存为草稿,默认 false

校验返回码:

  • 422: 字段超长 / 标签数量不在 1-3 / 媒体处理未完成 / highlights 时间点超出视频时长 / visibility 不在当前创作者已启用档位内
  • 403: 非创作者 / 媒体不属于当前用户
  • 404: 标签/被@用户不存在

响应数据:

字段 类型 说明
contentId string 内容ID
title string 标题
content string 正文
type string 内容类型
mediaIds int64[] 媒体ID列表
tags object[] 标签列表 [{id,name,source}]
mentions object[] 被 @ 用户列表 [{userId,nickname}]
location object 地点信息
visibility string 可见性档位 keyfree/junior/basic/senior/supreme
visibilityTierName string 可见档位的展示名创作者自定义名称free 时为 null
highlights array 视频章节标记列表
status string 状态draft / reviewing
isDraft boolean 是否草稿
createdAt timestamp 创建时间

CONTENT-02 获取广场 Feed

项目 说明
接口路径 GET /client/api/v1/content/feed
接口用途 获取广场信息流(双列瀑布流)
权限要求 公开(游客可访问)
涉及表 post, post_tag, post_media, tag, category, category_tag, user, media, content_boost, recommend_slot

接口逻辑:

  1. 根据 sort 参数选择排序策略:
    • recommend70%算法推荐 + 20%新创作者池 + 10%运营位(查询 recommend_slot 表,读)
    • latest按 post.created_at 倒序
    • hot按浏览量+点赞数加权排序
  2. 处理分类筛选categoryId 与 tag 二选一):
    • 如果传 categoryIdSELECT post.* FROM post JOIN post_tag pt ON pt.post_id = post.id JOIN category_tag ct ON ct.tag_id = pt.tag_id WHERE ct.category_id = :categoryId(读)
    • 如果传 tagSELECT post.* FROM post JOIN post_tag pt ON pt.post_id = post.id JOIN tag t ON t.id = pt.tag_id WHERE t.name = :tag(读)
    • 如果都不传:不加标签过滤,返回全部
  3. 查询 post 表 WHERE status=2已上架
  4. 关联查询 user 表 获取创作者信息profile.display_name、profile.avatar_url
  5. 关联查询 media 表 获取封面图/缩略图(读)
  6. 关联查询 content_boost 表 获取内容注水数据,外显数据 = 真实值 + boost值

    注:content_boost 用于单条内容的注水view/like/favoritecreator_boost 用于创作者主页的注水follower/subscriber后者在 API-08 CREATOR-02 中使用。

  7. 如果当前用户已登录,批量查询 user_subscription 表(按本页 author_id 列表 + 当前 viewer_id 一次查齐,避免 N+1逐条按 visibility=0 OR (订阅有效 AND user_subscription.tier_level >= post.visibility) 计算 isLocked。同时关联 subscription_tiername 作为 visibilityTierName 回填(读)
  8. 分页返回内容卡片列表

请求参数Query

字段 类型 必选 说明
sort string 排序方式recommend/latest/hot默认recommend
categoryId string 分类ID首页 Tab 分类筛选,内部通过 category_tag JOIN post_tag 匹配帖子)
tag string 标签筛选tag.name与 categoryId 二选一
page number 页码默认1
pageSize number 每页条数默认20

响应数据:

字段 类型 说明
list array 内容卡片列表
list[].contentId string 内容ID
list[].title string 标题
list[].coverUrl url 封面图URL
list[].blurCoverUrl url 模糊封面URL
list[].type string 类型post/video
list[].duration number 视频时长。type 为 video 时返回,非视频或无媒体时省略
list[].visibility string 可见性档位 keyfree/junior/basic/senior/supreme
list[].visibilityTierName string 该档位在当前创作者下的展示名free 时为 null
list[].visibilityTierPrice number 该档位价格free 时省略
list[].isPinned boolean 是否置顶
list[].hasHighlights boolean 是否包含视频章节标记(视频类型时)
list[].creator object 创作者信息
list[].creator.userId string 创作者ID
list[].creator.nickname string 创作者昵称
list[].creator.avatar url 创作者头像
list[].creator.isOnline boolean 是否在线
list[].viewCount number 浏览量含boost
list[].likeCount number 点赞数含boost
list[].favoriteCount number 收藏数含boost
list[].commentCount number 评论数
list[].isLocked boolean 是否需订阅/付费
list[].tags object[] 标签列表 [{id,name}]
list[].createdAt timestamp 发布时间
pagination object 分页信息

CONTENT-02b 获取关注 Feed

项目 说明
接口路径 GET /client/api/v1/content/feed/following
接口用途 首页"关注" tab返回当前用户已关注创作者发布的内容
权限要求 需登录JWT
涉及表 user_follow, post, post_media, media, user_profile, subscription_tier, user_subscription

接口逻辑:

  1. 从 JWT 取 viewer_id
  2. 查询 user_follow 表,取当前用户全部 followee_iddeleted_at IS NULL);若为空直接返回空列表
  3. 调用与 CONTENT-02 相同的 ListFeed,在 post 上追加 author_id = ANY($followeeIds) 过滤status=2按 published_at 倒序
  4. 组装逻辑与 CONTENT-02 第 4~7 步一致(作者资料、媒体封面/时长、订阅档位、isLocked 计算)
  5. 分页返回

请求参数Query 与 CONTENT-02 一致sort/categoryId/tag/page/pageSize

响应数据: 与 CONTENT-02 ContentFeedResp 完全一致list[] 为 FeedItem


CONTENT-03 获取内容详情

项目 说明
接口路径 GET /client/api/v1/content/:contentId
接口用途 获取单条内容详情
权限要求 按订阅档位判断
涉及表 post, post_tag, tag, post_media, media, user, view_history, post_like, post_favorite, user_subscription, content_boost

接口逻辑:

  1. 查询 post 表 根据 contentId 获取内容(读)
  2. 浏览去重 + 计数(同一事务):
    • INSERT INTO view_history (user_id, post_id, last_viewed_at) VALUES (:uid, :pid, NOW()) ON CONFLICT (user_id, post_id, view_date) DO NOTHING RETURNING id
    • 仅当 RETURNING 返回行数 > 0即当天首次浏览才执行 UPDATE post SET view_count = view_count + 1 WHERE id = :pid
    • 保证 post.view_countview_history 按日去重一致
  3. 关联查询 user 表 获取创作者信息(读)
  4. 查询 media 表 获取关联媒体信息(读)
  5. 查询 user_subscription 表 判断当前用户订阅状态(读):
    • 有权限签发媒体签名URL
    • 无权限返回模糊图URL、文字截断前2行、视频返回3秒模糊预览URL(media.preview_url)
  6. 查询 content_boost 表 获取注水数据(读)
  7. 查询 post_like 表 检查 isLikedWHERE user_id=me AND post_id=contentId
  8. 查询 post_favorite 表 检查 isFavorited
  9. 组装完整响应

路径参数:

字段 类型 必选 说明
contentId string 内容ID

响应数据:

字段 类型 说明
contentId string 内容ID
creator object 创作者信息
title string 标题
content string 正文无权限时截断前2行
type string 内容类型 post/video
images array 图片列表
images[].mediaId string 媒体ID
images[].url url 原图签名URL有权限时
images[].blurUrl url 模糊图URL
images[].thumbnailUrl url 缩略图URL
images[].isLocked boolean 是否锁定
videoUrl url 视频签名URL有权限时
videoBlurPreviewUrl url 3秒模糊预览URL无权限时
videoCoverUrl url 视频封面URL
videoDuration number 视频时长(秒)
highlights array 视频章节标记列表 [{timeOffsetSeconds,label}]
tags object[] 标签列表 [{id,name,source}]
mentions object[] 被 @ 用户列表 [{userId,nickname,avatar}]
location object 地点信息 {name,code}
visibility string 可见性档位 keyfree/junior/basic/senior/supreme
visibilityTierName string 该档位在内容作者下的展示名创作者自定义free 时为 null
visibilityTierPrice number 该档位月价代币free 时为 0— 用于无权限时引导订阅
isUnlocked boolean 当前用户是否已解锁(基于 user_subscription.tier_level ≥ post.visibility 且订阅有效)
isPinned boolean 是否置顶
viewCount number 浏览量含boost
likeCount number 点赞数含boost
favoriteCount number 收藏数含boost
commentCount number 评论数
isLiked boolean 是否已点赞
isFavorited boolean 是否已收藏
createdAt timestamp 发布时间

CONTENT-04 获取预设标签列表

项目 说明
接口路径 GET /client/api/v1/content/tags
接口用途 获取平台标签(系统预设 + 用户自定义热门标签)
权限要求 公开
涉及表 tag

接口逻辑:

  1. 查询 tag 表 WHERE is_active=1
  2. 根据 source 过滤source=1 仅系统预设source=2 仅用户自定义;不传则全部
  3. 根据 sort默认按 sort_order 升序sort=hot 时按 use_count DESC 排序
  4. 返回标签列表(含 source 与 useCount与 CONTENT-03 详情字段对齐)

请求参数Query

字段 类型 必选 说明
source int 1=系统预设 2=用户自定义;不传返回全部
sort string 排序:默认按 sort_orderhot=按 use_count 倒序

响应数据:

字段 类型 说明
tags array 标签列表
tags[].id string 标签ID
tags[].name string 标签名称
tags[].source int 1=系统预设 2=用户自定义
tags[].useCount number 累计使用次数(用于热度展示)

CONTENT-05 内容管理列表(创作者)

项目 说明
接口路径 GET /client/api/v1/content/manage
接口用途 创作者查看自己的内容列表
权限要求 创作者
涉及表 post, media

接口逻辑:

  1. 校验当前用户为创作者
  2. 查询 post 表 WHERE creator_id=当前用户,可选按 status 筛选(读)
  3. 关联查询 media 表 获取封面图(读)
  4. 分页返回,按 created_at 倒序

请求参数Query

字段 类型 必选 说明
status string 筛选draft/reviewing/published/rejected/removed
page number 页码
pageSize number 每页条数

响应数据:

字段 类型 说明
list array 内容列表
list[].contentId string 内容ID
list[].title string 标题
list[].coverUrl url 封面图URL
list[].type string 类型
list[].status string 状态
list[].rejectReason string 拒绝原因rejected时
list[].visibility string 可见性档位 keyfree/junior/basic/senior/supreme
list[].visibilityTierName string 档位展示名free 时为 null
list[].viewCount number 浏览量
list[].likeCount number 点赞数
list[].favoriteCount number 收藏数
list[].commentCount number 评论数
list[].isPinned boolean 是否置顶
list[].createdAt timestamp 发布时间
pagination object 分页信息

CONTENT-06 编辑内容

项目 说明
接口路径 PUT /client/api/v1/content/:contentId
接口用途 创作者修改已发布的内容
权限要求 创作者(只能编辑自己的)
涉及表 post, post_tag, post_media, category_tag

接口逻辑(全程单事务):

  1. 查询 post 表 根据 contentId 获取内容(读)
  2. 校验 post.creator_id = 当前用户
  3. 如果传 tags在同一事务内
    • DELETE FROM post_tag WHERE post_id = :contentId
    • 批量 INSERT INTO post_tag (post_id, tag_id) VALUES ...
  4. 如果传 mediaIds同样 DELETE FROM post_media WHERE post_id = :contentId 然后批量 INSERT 重建
  5. 更新 post 表 的 title/content/visibility/category_id 等字段(更新)
  6. 返回更新后的内容信息

路径参数:

字段 类型 必选 说明
contentId string 内容ID

请求参数Body

字段 类型 必选 说明
title string 标题,最长 30 字
content string 正文内容,最长 200 字
tags object[] 标签数组1-3 个;结构同 CONTENT-01每项 idname 二选一,仅传 name 时同名复用或新建自定义标签
mentions string[] 被 @ 用户的 userId 数组
location object 地点 {name, code},传 null 清空
visibility string 可见性档位 keyfree 或当前创作者已启用的档位之一(同 CONTENT-01 校验规则),不传则保持原值
highlights array 视频撸点(仅视频类型)
publishDraft boolean 草稿提交发布true 时把 status 从 0 改为 1 进入审核

响应数据: 同 CONTENT-01 响应结构


CONTENT-07 删除/下架内容

项目 说明
接口路径 DELETE /client/api/v1/content/:contentId
接口用途 创作者删除或下架内容
权限要求 创作者(只能操作自己的)
涉及表 post

接口逻辑:

  1. 查询 post 表 根据 contentId 获取内容(读)
  2. 校验 post.creator_id = 当前用户
  3. 更新 post 表 的 status=4已下架更新
  4. 返回成功

路径参数:

字段 类型 必选 说明
contentId string 内容ID

响应数据: null


CONTENT-08 点赞/取消点赞

项目 说明
接口路径 POST /client/api/v1/content/:contentId/like
接口用途 用户对内容点赞或取消Toggle
权限要求 需登录
涉及表 post_like, post

接口逻辑:

  1. 查询 post_like 表 检查是否已有记录WHERE user_id=me AND post_id=contentId
  2. 已点赞 → 取消
    • 软删除 post_like 表 中的记录(更新 deleted_at
    • 更新 post 表 的 like_count-1更新
  3. 未点赞 → 点赞
    • 写入 post_like 表(写)
    • 更新 post 表 的 like_count+1更新
  4. 返回当前点赞状态和最新点赞数

路径参数:

字段 类型 必选 说明
contentId string 内容ID

响应数据:

字段 类型 说明
isLiked boolean 当前点赞状态
likeCount number 最新点赞数

CONTENT-09 收藏/取消收藏

项目 说明
接口路径 POST /client/api/v1/content/:contentId/favorite
接口用途 用户收藏或取消收藏内容Toggle
权限要求 需登录
涉及表 post_favorite, post

接口逻辑:

  1. 查询 post_favorite 表 检查是否已有记录WHERE user_id=me AND post_id=contentId
  2. 已收藏 → 取消
    • 软删除 post_favorite 表 中的记录(更新 deleted_at
    • 更新 post 表 的 favorite_count-1更新
  3. 未收藏 → 收藏
    • 写入 post_favorite 表(写)
    • 更新 post 表 的 favorite_count+1更新
  4. 返回当前收藏状态和最新收藏数

路径参数:

字段 类型 必选 说明
contentId string 内容ID

响应数据:

字段 类型 说明
isFavorited boolean 当前收藏状态
favoriteCount number 最新收藏数

CONTENT-10 获取收藏列表

项目 说明
接口路径 GET /client/api/v1/content/favorites
接口用途 用户查看自己收藏的内容
权限要求 需登录
涉及表 post_favorite, post, media, user

接口逻辑:

  1. 查询 post_favorite 表 WHERE user_id=当前用户,按 created_at 倒序(读)
  2. 关联查询 post 表 获取内容信息(读)
  3. 关联查询 media 表 获取封面图(读)
  4. 关联查询 user 表 获取创作者信息(读)
  5. 分页返回,结构同 CONTENT-02 列表,多一个 favoritedAt 字段

请求参数Query

字段 类型 必选 说明
page number 页码
pageSize number 每页条数

响应数据: 同 CONTENT-02 列表结构,额外增加 favoritedAttimestamp字段


CONTENT-11 获取首页分类列表

项目 说明
接口路径 GET /client/api/v1/content/categories
接口用途 获取首页分类 Tab 列表(美妆/旅行/美食等),用于首页顶部 Tab 渲染
权限要求 公开
涉及表 category

接口逻辑:

  1. 查询 category 表 WHERE is_active=1按 sort_order 正序(读)
  2. 返回分类列表(仅返回 id/name/icon 等展示字段,不返回标签关联细节)
  3. 前端拿到 categoryId 后,请求 CONTENT-02 /client/api/v1/content/feed?categoryId=xxx 获取该分类下的内容列表

分类与帖子的关联方式:

  • 分类在后台配置时绑定多个预设标签(关系存于 category_tag 关联表)
  • 帖子的标签存于 post_tag 关联表,帖子本身不直接绑定 category
  • 首页按分类取内容 = JOIN post_tag pt ON pt.post_id = post.id JOIN category_tag ct ON ct.tag_id = pt.tag_id WHERE ct.category_id = :categoryId

请求参数:

响应数据:

字段 类型 说明
categories array 分类列表
categories[].categoryId string 分类ID
categories[].name string 分类名称(如:美妆、旅行、美食)
categories[].icon url 分类图标URL可空
categories[].sortOrder number 排序序号

CONTENT-12 创建自定义标签

项目 说明
接口路径 POST /client/api/v1/content/tags/custom
接口用途 标签管理场景下预先创建自定义标签。发布/编辑内容CONTENT-01/06已支持在 tags[].name 内联新建,常规发布流程无需先调本接口
权限要求 创作者
涉及表 tag

接口逻辑:

  1. 校验当前用户为创作者
  2. 校验 name 长度 1-32 字
  3. 查询 tag 表 WHERE name=输入值,若已存在直接返回该 tag去重
  4. 命中敏感词库 → 422 拒绝
  5. 写入 tagsource=2created_by=当前用户is_active=1
  6. 返回新创建的 tag

请求参数Body

字段 类型 必选 说明
name string 标签名1-32 字

响应数据:

字段 类型 说明
id int64 标签ID
name string 标签名
source number 1=系统 2=自定义