12 KiB
12 KiB
ADMIN-API-11 内容管理(POST-A)
共 4 个接口:POST-A01~04
本模块用于运营全量浏览/排查帖子,与 ADMIN-API-02(待审核工作流)和 ADMIN-API-06(数据注水操作)职责分离:
- 02 内容审核:只看
status=1 待审核队列 - 06 数据注水:只看
content_boost表里已注水的记录 - 11 内容管理(本模块):全量帖子(含已上架、已下架、已注水未注水),核心目的是反向定位疑似自刷量的创作者 —— 通过外显/真实数据并列返回 + 比值排序筛选发现异常帖子
内容类型枚举
与 API-04 内容系统 对齐:仅 2 类。
| DB值 | API值 | 说明 |
|---|---|---|
| 1 | post | 动态(图文,1-9 张图) |
| 2 | video | 视频(不区分时长) |
状态枚举
| DB值 | API值 | 说明 |
|---|---|---|
| 0 | draft | 草稿 |
| 1 | reviewing | 待审核 |
| 2 | published | 已上架 |
| 3 | rejected | 审核拒绝 |
| 4 | removed | 已下架 |
POST-A01 帖子列表(全量)
| 项目 | 说明 |
|---|---|
| 接口路径 | GET /admin/api/v1/posts |
| 接口用途 | 运营全量浏览/排查帖子,支持按比值筛选定位疑似注水号 |
| 权限要求 | 超级管理员 / 内容审核员 / 运营专员(只读:客服) |
| 涉及表 | post, user, profile, content_boost, post_tag, tag, category, category_tag, media |
接口逻辑:
- 校验管理员权限(permission_key:
content:list) - 拼接 WHERE:按
creatorId / status / visibility / categoryId / tagId / keyword / startDate / endDate / boosted过滤 - 关联
content_boost(LEFT JOIN)取注水值;外显字段 = 真实值 + boost值(仅is_active=1时叠加) - 计算比值列(
like/view、comment/view),用于筛选与排序- 当
view_count = 0时比值置 0 避免除零
- 当
- 应用
minViews / minLikes / minComments / likeViewRatioMin / commentViewRatioMin数值下限过滤 - 按
sortBy排序(白名单见下),默认published_at desc - 关联
user + profile取创作者信息、media取封面、post_tag + tag取标签 - 分页返回
性能提示(DBA):
view_count / like_count / comment_count已有索引,但like/view比值类筛选在大数据量下需要在post表上新增表达式索引或预计算列(建议预计算列 + 触发器或物化),否则WHERE (like_count::float/view_count) > 0.5会全表扫。
请求参数(Query):
通用参数
page / pageSize / startDate / endDate / keyword见 README.md Query 约定。
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| creatorId | string | 否 | 按创作者 userId 筛选(最关键,定位疑似自刷号后看 ta 的所有帖子) |
| status | int | 否 | post.status:0/1/2/3/4 |
| visibility | string | 否 | free/junior/basic/senior/supreme |
| categoryId | string | 否 | 分类筛选(通过 category_tag JOIN post_tag) |
| tagId | string | 否 | 单标签筛选 |
| keyword | string | 否 | title / body 模糊匹配 |
| startDate | date | 否 | 按 published_at 区间起 |
| endDate | date | 否 | 按 published_at 区间止 |
| boosted | bool | 否 | 是否已设注水:true=已注水 / false=未注水 / 不传=全部 |
| minViews | int | 否 | 真实浏览量下限 |
| minLikes | int | 否 | 真实点赞数下限 |
| minComments | int | 否 | 真实评论数下限 |
| likeViewRatioMin | float | 否 | 真实点赞/浏览 比值下限(识别"浏览低但点赞暴高"典型注水特征) |
| commentViewRatioMin | float | 否 | 真实评论/浏览 比值下限 |
| sortBy | string | 否 | 排序字段(白名单):published_at / created_at / view_count / like_count / comment_count / favorite_count / like_view_ratio / comment_view_ratio |
| sortOrder | string | 否 | asc / desc,默认 desc |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| list | array | 帖子列表 |
| list[].postId | string | 帖子ID |
| list[].title | string | 标题 |
| list[].contentPreview | string | 正文截断(前 100 字) |
| list[].coverUrl | url | 封面图URL |
| list[].type | string | 类型:post/video |
| list[].visibility | string | 可见性档位 |
| list[].status | string | 状态:draft/reviewing/published/rejected/removed |
| list[].publishedAt | timestamp | 发布时间(已上架才有) |
| list[].createdAt | timestamp | 创建时间 |
| list[].creator | object | 创作者信息 |
| list[].creator.userId | string | 创作者 userId |
| list[].creator.nickname | string | 昵称 |
| list[].creator.avatar | url | 头像 |
| list[].category | object | 分类(可空){id, name} |
| list[].tags | array | 标签 [{id, name}] |
| list[].stats | object | 统计数据(核心:真实/外显并列) |
| list[].stats.viewCount | number | 外显浏览量(真实+boost) |
| list[].stats.likeCount | number | 外显点赞数 |
| list[].stats.favoriteCount | number | 外显收藏数 |
| list[].stats.commentCount | number | 外显评论数 |
| list[].stats.realViewCount | number | 真实浏览量 |
| list[].stats.realLikeCount | number | 真实点赞数 |
| list[].stats.realFavoriteCount | number | 真实收藏数 |
| list[].stats.realCommentCount | number | 真实评论数 |
| list[].stats.likeViewRatio | float | 真实点赞/浏览 比值(保留 4 位小数) |
| list[].stats.commentViewRatio | float | 真实评论/浏览 比值 |
| list[].boostInfo | object|null | 注水信息,未注水返回 null |
| list[].boostInfo.isActive | bool | 注水是否启用 |
| list[].boostInfo.boostViewCount | number | 注水浏览量 |
| list[].boostInfo.boostLikeCount | number | 注水点赞数 |
| list[].boostInfo.boostFavoriteCount | number | 注水收藏数 |
| list[].boostInfo.autoStopAt | timestamp | 注水自动停止时间 |
| list[].boostInfo.operatedBy | string | 设置注水的管理员 ID |
| list[].takedownReason | string|null | 下架原因(status=4 时) |
| pagination | object | 分页信息 |
POST-A02 帖子详情
| 项目 | 说明 |
|---|---|
| 接口路径 | GET /admin/api/v1/posts/:postId |
| 接口用途 | 查看单条帖子完整信息(含媒体、审核历史、注水历史、举报历史) |
| 权限要求 | 同 POST-A01 |
| 涉及表 | post, user, profile, content_boost, post_media, media, post_tag, tag, post_highlight, post_mention, audit_log, report |
接口逻辑:
- 校验权限(permission_key:
content:read) - 查
post主体;不存在 → 404 - 关联
user + profile取创作者 - 关联
post_media + media取所有媒体(运营侧拿原图签名 URL,不走 visibility 锁) - 关联
post_tag + tag取标签、post_highlight取视频章节标记、post_mention取被 @ 用户 - 关联
content_boost取当前注水状态 - 关联
admin_operation_log取该 postId 的所有审核/下架/注水操作历史(按时间倒序) - 关联
report取该 postId 的举报记录(仅返回数量与最近 5 条摘要) - 组装完整响应
路径参数:
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| postId | string | 是 | 帖子ID |
响应数据:
在 POST-A01 单条结构基础上扩展:
| 字段 | 类型 | 说明 |
|---|---|---|
| (POST-A01 列表项全部字段) | — | — |
| body | string | 完整正文 |
| medias | array | 媒体列表 |
| medias[].mediaId | string | 媒体ID |
| medias[].type | string | image/video |
| medias[].url | url | 原图/原视频签名URL |
| medias[].thumbnailUrl | url | 缩略图 |
| medias[].duration | number | 视频时长(秒,仅视频) |
| highlights | array | 视频章节标记 [{timeOffsetSeconds, label}] |
| mentions | array | @ 用户 [{userId, nickname, avatar}] |
| location | object|null | 地点 {name, code} |
| operationHistory | array | 操作历史(来自 admin_operation_log) |
| operationHistory[].action | string | approve/reject/takedown/set_boost/close_boost |
| operationHistory[].operatorId | string | 管理员ID |
| operationHistory[].operatorName | string | 管理员昵称 |
| operationHistory[].reason | string | 备注/原因 |
| operationHistory[].createdAt | timestamp | — |
| reports | object | 举报概要 |
| reports.total | number | 举报总数 |
| reports.recent | array | 最近 5 条 [{reportId, reason, createdAt}] |
POST-A03 创作者作品列表
| 项目 | 说明 |
|---|---|
| 接口路径 | GET /admin/api/v1/creators/:creatorId/posts |
| 接口用途 | 查看某创作者的全部帖子(路径锁定 creatorId,便于 RBAC 与审计区分) |
| 权限要求 | 同 POST-A01 |
| 涉及表 | 同 POST-A01 |
接口逻辑:
- 校验权限(permission_key:
content:list) - 校验
creatorId存在且role=2(创作者);否则 404 - 复用 POST-A01 查询逻辑,固定
creatorId = :creatorId - Query 参数除
creatorId外其余与 POST-A01 一致
路径参数:
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| creatorId | string | 是 | 创作者ID |
请求参数(Query): 同 POST-A01,去掉 creatorId
响应数据: 同 POST-A01
实现层可直接复用 POST-A01 处理函数,仅在外层包一层
creatorId注入。
POST-A04 强制下架帖子
| 项目 | 说明 |
|---|---|
| 接口路径 | PATCH /admin/api/v1/posts/:postId/takedown |
| 接口用途 | 运营对已上架帖子执行强制下架(与 AUDIT-03 拒绝待审稿区分) |
| 权限要求 | 超级管理员 / 内容审核员 |
| 涉及表 | post, admin_operation_log, notification |
接口逻辑:
- 校验权限(permission_key:
content:takedown) - 同一事务内:
SELECT ... FOR UPDATE取post旧值,校验status IN (2 published),否则 409UPDATE post SET status = 4, takedown_reason = :reason, takedown_by = :adminId, takedown_at = NOW()- 写入
admin_operation_log(action:takedown_post,target_type:post,old_value/new_value 写入完整 JSONB)
- 若
notifyCreator=true,写入notification(type=系统通知) - 返回更新后的 post 摘要
路径参数:
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| postId | string | 是 | 帖子ID |
请求参数(Body):
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
| reason | string | 是 | 下架原因,5-200 字 |
| notifyCreator | bool | 否 | 是否向创作者发送系统通知,默认 true |
响应数据:
| 字段 | 类型 | 说明 |
|---|---|---|
| postId | string | 帖子ID |
| status | string | removed |
| takedownReason | string | 下架原因 |
| takedownAt | timestamp | 下架时间 |
校验返回码:
- 403:权限不足
- 404:帖子不存在
- 409:帖子状态不允许下架(非 published)
- 422:reason 长度不合法
涉及表与字段补充说明
| 表 | 本模块用法 |
|---|---|
post |
主表。需补字段:takedown_reason VARCHAR(200)、takedown_by UUID、takedown_at TIMESTAMPTZ |
content_boost |
LEFT JOIN 取注水值;is_active=1 时外显值叠加 |
admin_operation_log |
写入 takedown 审计;POST-A02 详情读取该帖的全部历史 |
report |
POST-A02 详情读取该帖举报概要 |
notification |
POST-A04 通知创作者 |
建议预计算列(DBA 评估):
post.like_view_ratio NUMERIC(10,4)—— UPDATE 触发器维护post.comment_view_ratio NUMERIC(10,4)- 索引:
idx_post_lv_ratio (like_view_ratio DESC) WHERE status = 2