txagent-y/docs/dev/api/API-10-搜索通知举报.md

454 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# API-10 搜索·通知·举报·反馈SEARCH + NOTI + REPORT + FEEDBACK + HISTORY
> 共 10 个接口SEARCH-01~02 + NOTI-01~03 + REPORT-01~02 + FEEDBACK-01 + HISTORY-01~02
>
> **注:** 搜索历史由**客户端本地存储**,后端不提供读写接口(原 SEARCH-03/04 已下线)。
---
## 搜索系统search
#### SEARCH-01 综合搜索
| 项目 | 说明 |
|------|------|
| 接口路径 | GET /client/api/v1/search |
| 接口用途 | 按关键词搜索用户 / 帖子 / 标签(对应搜索页 4 个 tab |
| 权限要求 | 需登录 |
| 涉及表 | user, user_profile, post, post_media, tag, user_follow, search_trending |
**接口逻辑:**
1. 接收搜索关键词(前端 300ms 防抖),后端会 trim 前后空格;校验 `type` 合法后,将关键词记录到服务内存聚合器,后台默认每 2 分钟批量写入 `search_trending` 并累加 `search_count`;若待写入搜索 hit 达到 1000 次或不同关键词达到 1000 个,会提前异步 flush统计写入失败或瞬时超出 4096 个不同关键词聚合上限不影响搜索结果返回)
2.`type` 分流:
- `type=users`:在 `user`(普通用户 + 创作者)+ `user_profile` 中模糊匹配 `username / display_name / user_no`;分页,仅返回 active 且未删除用户
- `type=posts`:在 `post`status=published, deleted_at IS NULL中模糊匹配 `title`;排序:匹配度 > 点赞数 > 发布时间;关联 `post_media` 取封面/时长、`user` 取创作者;分页
- `type=tags`:在 `tag` 中模糊匹配 `name`,附带 `post_count` 冗余字段;按 `post_count` 倒序;分页
- `type=all`(默认):并行执行上述三段,每段固定 limitusers=3, posts=10, tags=10忽略 `page/pageSize`
3. 用户段返回 `isCreator`;创作者用户会补充当前用户视角的 `isFollowing`、`followerCount`、`postCount`,普通用户这些创作者指标返回零值 / false
4. **搜索历史不写后端**,由客户端本地保存;后端只异步沉淀聚合后的搜索词和次数,不提供个人搜索历史读写
**请求参数Query**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| keyword | string | 是 | 搜索关键词 |
| type | string | 否 | `all`(默认)/ `users` / `posts` / `tags` |
| page | number | 否 | 页码,默认 1`type=all` 时忽略) |
| pageSize | number | 否 | 每页条数,默认 20`type=all` 时忽略) |
**响应数据:**
顶层结构按 `type` 决定返回哪些段;`type=all` 时三段齐全,其他 type 仅返回对应段。
| 字段 | 类型 | 说明 |
|------|------|------|
| users | object/null | 用户段(`type=users` 或 `all` 时存在) |
| users.list | array | 用户列表,包含普通用户和创作者 |
| users.list[].userId | string | 用户 ID |
| users.list[].userNo | string | 9 位用户编号 |
| users.list[].nickname | string | 昵称 |
| users.list[].avatar | url | 头像 |
| users.list[].description | string | 简介;创作者优先返回创作者简介,普通用户返回用户简介 |
| users.list[].isCreator | boolean | 是否创作者 |
| users.list[].followerCount | number | 粉丝数;普通用户为 0 |
| users.list[].postCount | number | 内容数;普通用户为 0 |
| users.list[].isFollowing | boolean | 当前用户是否已关注;普通用户为 false |
| users.pagination | object | 分页信息 |
| posts | object/null | 帖子段(`type=posts` 或 `all` 时存在) |
| posts.list | array | 帖子列表,字段与 CONTENT-02 列表项一致 |
| posts.list[].postId | string | 帖子 ID |
| posts.list[].title | string | 标题 |
| posts.list[].type | string | `post / video` |
| posts.list[].coverUrl | url | 封面图 |
| posts.list[].blurCoverUrl | url | 模糊封面(锁定态使用) |
| posts.list[].duration | number | 时长(秒,视频类) |
| posts.list[].isLocked | boolean | 是否锁定(需订阅/付费) |
| posts.list[].visibility | number | 0=免费 1~4=付费档位 |
| posts.list[].visibilityTierName | string | 档位名称 |
| posts.list[].likeCount | number | 点赞数 |
| posts.list[].creator | object | 创作者简要信息userId/nickname/avatar |
| posts.pagination | object | 分页信息 |
| tags | object/null | 标签段(`type=tags` 或 `all` 时存在) |
| tags.list | array | 标签列表 |
| tags.list[].tagName | string | 标签名(不带 # |
| tags.list[].postCount | number | 关联内容数 |
| tags.pagination | object | 分页信息 |
---
#### SEARCH-02 获取热门搜索词
| 项目 | 说明 |
|------|------|
| 接口路径 | GET /client/api/v1/search/trending |
| 接口用途 | 获取热门搜索词列表搜索历史页底部「热门搜索」chips |
| 权限要求 | 公开 |
| 涉及表 | search_trending |
**接口逻辑:**
1. 客户端接口读取服务内存缓存,不每次直查数据库
2. 服务启动时从 `search_trending` 加载热搜词,之后每 2 分钟自动刷新一次;刷新失败保留旧缓存
3. 缓存数据来源:`search_trending` 表中 `is_hot=1 AND is_active=1 AND deleted_at IS NULL` 的词
4. 排序:运营手动词 `is_manual=1` 优先,按 `sort_order ASC`;自动热词按 `search_count DESC`;再按 `updated_at DESC, id ASC` 兜底,取前 10
5. `search_count` 由搜索接口异步聚合写入,默认每 2 分钟批量落库,待写入搜索 hit 达到 1000 次或不同关键词达到 1000 个时提前 flush接口缓存仍按 2 分钟刷新节奏展示,搜索量很低时展示延迟可能接近 4 分钟
**请求参数:**
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| keywords | string[] | 热门搜索词列表(最多 10 个) |
---
## 通知系统notification
### relatedId 语义说明
| type | relatedType | relatedId 含义 |
|------|-------------|---------------|
| like | post | 被点赞的内容 IDpost._id |
| comment | post | 被评论的内容 IDpost._id |
| mention | post | @ 提及所在的内容 IDpost._id |
| creator_publish | post | 创作者新发布内容 IDpost._id |
| publish_success | post | UP 主自己发布成功的内容 IDpost._id |
| publish_success | media | 视频上传处理完成并进入待审核的媒体 IDmedia.id |
| system | media | 分片视频异步处理失败的媒体 IDmedia.id |
| follow | user | 关注者的用户 IDuser._id |
| subscription | user | 订阅者的用户 IDuser._id |
| purchase | order | 订阅订单 IDsubscription_order._id |
| ppv_unlock | order | PPV 消息 IDppv_message._id |
| system | — | null系统通知无关联对象 |
#### NOTI-01 获取通知列表
| 项目 | 说明 |
|------|------|
| 接口路径 | GET /client/api/v1/notification/list |
| 接口用途 | 获取通知中心消息 |
| 权限要求 | 需登录 |
| 涉及表 | notification, user |
**接口逻辑:**
1. 查询 `notification`user_id 匹配,按 created_at 倒序)
2. 支持按 category 筛选interaction/subscription/system
3. 相同类型聚合(如"XXX等3人点赞了你的内容"
4. 关联 `user` 表 获取触发者信息
**请求参数Query**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| category | string | 否 | 分类all/interaction/subscription/system默认all |
| page | number | 否 | 页码默认1 |
| pageSize | number | 否 | 每页条数默认20 |
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| list | array | 通知列表 |
| list[].notificationId | string | 通知ID |
| list[].category | string | 分类 |
| list[].type | string | 具体类型like/comment/mention/creator_publish/publish_success/subscription/follow/purchase/ppv_unlock/system |
| list[].title | string | 通知标题 |
| list[].content | string | 通知内容 |
| list[].sender | object/null | 触发者信息 |
| list[].sender.userId | string | 触发者ID |
| list[].sender.nickname | string | 昵称 |
| list[].sender.avatar | url | 头像 |
| list[].coverUrl | url | 关联内容封面;当 relatedType=post 且可解析到内容封面时返回 |
| list[].relatedId | string | 关联对象ID含义见下表 |
| list[].relatedType | string | 关联类型post/user/order/media |
| list[].isRead | boolean | 是否已读 |
| list[].createdAt | timestamp | 创建时间 |
| pagination | object | 分页信息 |
---
#### NOTI-02 标记已读
| 项目 | 说明 |
|------|------|
| 接口路径 | PUT /client/api/v1/notification/read |
| 接口用途 | 标记通知为已读(单条/全部) |
| 权限要求 | 需登录 |
| 涉及表 | notification |
**接口逻辑:**
1. 如果传了 notificationIds更新指定通知的 is_read=1
2. 如果传了 category更新该分类所有通知的 is_read=1
3. 如果都不传,更新该用户所有通知的 is_read=1
**请求参数Body**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| notificationIds | string[] | 否 | 通知ID数组不传表示全部标记 |
| category | string | 否 | 按分类标记全部已读 |
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| readCount | number | 本次标记已读的数量 |
---
#### NOTI-03 获取未读数
| 项目 | 说明 |
|------|------|
| 接口路径 | GET /client/api/v1/notification/unread-count |
| 接口用途 | 获取各分类的未读通知数 |
| 权限要求 | 需登录 |
| 涉及表 | notification |
**接口逻辑:**
1. 聚合查询 `notification`user_id 匹配is_read=0按 category 分组 count
**请求参数:**
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| total | number | 总未读数 |
| interaction | number | 互动未读数 |
| subscription | number | 订阅未读数 |
| system | number | 系统未读数 |
---
## 浏览历史history
#### HISTORY-01 获取浏览历史
| 项目 | 说明 |
|------|------|
| 接口路径 | GET /client/api/v1/history/views |
| 接口用途 | 获取用户的内容浏览历史 |
| 权限要求 | 需登录 |
| 涉及表 | view_history, post, user, media |
**接口逻辑:**
1. 查询 `view_history` 表 WHERE user_id=当前用户,按 last_viewed_at 倒序,分页(读)
2. 关联查询 `post` 表 获取帖子信息(读)
3. 关联查询 `user` 表 获取创作者信息(读)
4. 关联查询 `media` 表 获取封面/缩略图(读)
5. 返回浏览历史列表
**请求参数Query**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| page | number | 否 | 页码默认1 |
| pageSize | number | 否 | 每页条数默认20 |
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| list | array | 浏览记录列表 |
| list[].postId | string | 帖子ID |
| list[].title | string | 帖子标题 |
| list[].coverUrl | url | 封面图URL |
| list[].type | string | 内容类型post/video |
| list[].creator | object | 创作者信息 |
| list[].creator.userId | string | 创作者ID |
| list[].creator.nickname | string | 昵称 |
| list[].creator.avatar | url | 头像 |
| list[].lastViewedAt | timestamp | 最后浏览时间 |
| pagination | object | 分页信息 |
---
#### HISTORY-02 清除浏览历史
| 项目 | 说明 |
|------|------|
| 接口路径 | DELETE /client/api/v1/history/views |
| 接口用途 | 清除用户的浏览历史 |
| 权限要求 | 需登录 |
| 涉及表 | view_history |
**接口逻辑:**
1. 软删除 `view_history` 表 中该用户的所有记录(批量更新 deleted_at=当前时间)
**请求参数:**
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| success | number | 1=成功 |
---
## 举报系统report
#### REPORT-01 举报内容
| 项目 | 说明 |
|------|------|
| 接口路径 | POST /client/api/v1/report/content |
| 接口用途 | 用户举报不当内容(苹果审核要求) |
| 权限要求 | 需登录 |
| 涉及表 | report |
**接口逻辑:**
1. 校验被举报内容存在(读 `post` 表)
2. 检查是否重复举报(同一用户对同一内容)
3. 优先读取 `imageMediaIds`,查询 `media` 表校验媒体存在、归属当前用户、已处理完成且为图片(读)
4. 若未传 `imageMediaIds`,兼容读取旧字段 `images`
5. 写入 `report`type=content/userstatus=1待处理`images` 保存举报截图 URL 列表);同一用户对同一对象已有待处理举报时,返回原举报记录并更新最新原因、说明和截图
**请求参数Body**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| targetId | string | 是 | 被举报对象ID |
| targetType | string | 是 | 举报对象类型content/user |
| reason | string | 是 | 举报类型porn/violence/harassment/fraud/other |
| description | string | 否 | 补充说明 |
| imageMediaIds | string[] | 否 | 新字段,举报截图媒体 ID 列表;优先于 `images` 生效 |
| images | string[] | 否 | 旧字段,举报截图 URL 列表兼容保留deprecated |
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| reportId | string | 举报记录ID |
| message | string | "举报已提交,平台将通过机器审核+人工审核处理" |
---
#### REPORT-02 举报IM消息
| 项目 | 说明 |
|------|------|
| 接口路径 | POST /client/api/v1/report/message |
| 接口用途 | 用户举报私信中的不当消息(苹果审核要求) |
| 权限要求 | 需登录 |
| 涉及表 | report, message |
**接口逻辑:**
1. 校验被举报消息存在且用户是该会话参与者(读 `message` + `conversation`
2. 检查是否重复举报
3. 写入 `report`type=messagestatus=1待处理同一用户对同一消息已有待处理举报时返回原举报记录并更新最新原因、说明
**请求参数Body**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| messageId | string | 是 | 被举报消息ID |
| reason | string | 是 | 举报类型porn/violence/harassment/fraud/other |
| description | string | 否 | 补充说明 |
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| reportId | string | 举报记录ID |
| message | string | "举报已提交" |
---
## 反馈系统feedback
#### FEEDBACK-01 提交帮助与反馈
| 项目 | 说明 |
|------|------|
| 接口路径 | POST /client/api/v1/feedback |
| 接口用途 | 用户在“我的 > 帮助与反馈”页提交问题描述和截图 |
| 权限要求 | 需登录 |
| 涉及表 | user_feedback |
**接口逻辑:**
1. 从登录 token 解析当前用户 `userId`
2. 校验 `description` 非空,去掉首尾空白
3. 优先读取 `imageMediaIds`,查询 `media` 表校验媒体存在、归属当前用户、已处理完成且为图片(读)
4. 若未传 `imageMediaIds`,兼容读取旧字段 `images`
5. 对最终图片列表做空串过滤
6. 写入 `user_feedback` 表,保存 `user_id / description / images`
**请求参数Body**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| description | string | 是 | 反馈描述 |
| imageMediaIds | string[] | 否 | 新字段,反馈截图媒体 ID 列表;优先于 `images` 生效 |
| images | string[] | 否 | 旧字段,反馈截图 URL 列表兼容保留deprecated |
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| feedbackId | string | 反馈记录 ID |
---
## 异常日志error-log
#### ERRORLOG-01 上报客户端异常日志
| 项目 | 说明 |
|------|------|
| 接口路径 | POST /client/api/v1/uploadlogs |
| 接口用途 | 客户端在出现白屏、崩溃、接口异常等问题时,上报错误内容和运行环境,供后台排查 |
| 权限要求 | 公开(可未登录调用);若请求头带有效 Bearer Token则自动关联当前用户 |
| 涉及表 | client_error_log |
**接口逻辑:**
1.`deviceId`、`contents`、`appVersion`、`osType`、`stack` 做首尾空白裁剪
2. 校验 `deviceId`、`contents` 必填;`deviceId` 最大 128 字符,`appVersion`/`osType` 最大 32 字符
3. 不做服务端去重;每次调用都单独写入一条 `client_error_log`
4. 若请求头带有效 Bearer Token则从 token 解析 `userId` 并写入;否则 `user_id` 为空
5. 返回新写入的日志 ID
> **说明:**
> 1. 请求体不接受 `userId`、`username` 等显式用户字段,用户关联只信登录态 token。
> 2. `stack` 为可选字段,若客户端有堆栈信息应尽量完整上传。
**请求参数Body**
| 字段 | 类型 | 必选 | 说明 |
|------|------|------|------|
| deviceId | string | 是 | 设备号,用于后台按设备维度排查 |
| contents | string | 是 | 异常摘要/错误内容 |
| appVersion | string | 否 | App 版本号 |
| osType | string | 否 | 系统类型,如 `ios` / `android` / `h5` |
| stack | string | 否 | 异常堆栈或补充诊断信息 |
**请求示例:**
```json
{
"deviceId": "qa-uploadlogs-dev-20260420-001",
"contents": "App launch blank screen on boot",
"appVersion": "9.9.1-test",
"osType": "ios",
"stack": "Error: bootstrap failed\n at App.start (app.js:42)\n at main (index.js:7)"
}
```
**响应数据:**
| 字段 | 类型 | 说明 |
|------|------|------|
| logId | string | 异常日志记录 ID |
**响应示例:**
```json
{
"code": 0,
"message": "success",
"data": {
"logId": "48faa79e-ca02-4030-a002-c953dabdeea2"
}
}
```