Files
FruitServer/docs/api-phase1.md
T
Vicky b3179fc836 refactor: 后端标识统一为 FruitShorts,并落地 05 离线下载
为什么:
1) 产品已定名 FruitShorts,但后端仍是 reelshort 系列标识 —— Java 包 com.reelshort、
   模块名与 jar 名 reelshort-*、spring.application.name、配置前缀 reelshort.*,
   这些会出现在启动日志、命令行和 IDE 里,与产品名不一致。本次整体替换为 fruitshorts,
   数据库连接名上一轮已改名,保持不变。
2) 第一阶段下一批第 1 步(05 离线下载)要求把下载授权(设备绑定/有效期/吊销)、
   下载任务与断点续传、配额与过期清理、仅 Wi-Fi 规则做成完整接口,本提交一并落地。
3) 顺带修复:schema-phase1.sql 里 playback_ticket / order_event 仍按普通表定义唯一索引,
   表分区化之后重跑脚本必定报错(分区表的唯一索引必须包含分区键)。改为在本脚本里就
   按分区表定义,并验证脚本可重复执行。
2026-10-15 11:44:26 +08:00

265 lines
12 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.
# 第一阶段接口清单(后台管理 + 客户端)
> 版本 v1.0 · 配套 [PRD](./prd-phase1.md) 与 [后端设计](./backend-design-phase1.md)
> 状态说明:**已实现** = 代码已落地可联调;**下一批** = 表结构与契约已冻结,代码待补。
## 0. 通用约定
**服务入口**:后台管理 `fruitshorts-admin-api` 只暴露 `/admin/**`(8080);
客户端 `fruitshorts-app-api` 只暴露 `/app/**` 与媒资 `/files/**`(8081)。
**统一响应体**
```json
{ "code": 0, "message": "ok", "data": {} }
```
`code = 0` 成功;非 0 时 `message` 可直接展示给用户。分页统一为 `{ total, list }`。
**鉴权**:`Authorization: Bearer <token>`。
管理端与客户端用**两套独立密钥**签发,管理端 token 打到客户端接口会直接被拒。
客户端 `access token` 默认 2 小时,`refresh token` 30 天且每次刷新轮换。
**设备标识**:客户端建议所有请求都带 `X-Device-Id`,用于下载授权、并发播放限制与风控。
**主要错误码**
| code | 含义 | 客户端处理 |
| --- | --- | --- |
| 0 | 成功 | — |
| 400 | 参数校验失败 | 提示 message |
| 401 | 未登录或登录过期 | 用 refresh token 换新 token,失败则跳登录 |
| 403 | 无权限 | 提示无权限 |
| 429 | 请求过于频繁 | 提示稍后再试(验证码频控走这里) |
| 100000 | 账号或密码错误 | 提示 |
| 100001 | 账号已停用 | 展示封禁申诉入口 |
| 100002 | 金币余额不足 | 拉起充值 |
| 100003 | 剧集未发布或不存在 | 返回上一页 |
| 100005 | 订单已支付或已关闭 | 刷新订单状态 |
| 200002 | 数据不存在 | 提示 |
| 200003 | 当前状态不允许该操作 | 提示 |
## 1. 客户端接口(/app/**)
### 1.1 账号(已实现)
| 方法 | 路径 | 鉴权 | 说明 |
| --- | --- | --- | --- |
| POST | `/app/auth/otp` | 匿名 | 发送验证码。`scene` = REGISTER/LOGIN/RESET_PWD/BIND |
| POST | `/app/auth/register` | 匿名 | 手机号/邮箱注册,返回令牌 |
| POST | `/app/auth/login` | 匿名 | 验证码或密码登录 |
| POST | `/app/auth/refresh` | 匿名 | 用 refresh token 换新令牌并轮换 |
| POST | `/app/auth/logout` | 可选 | 登出;`allDevices=true` 吊销全部会话 |
发送验证码:
```json
POST /app/auth/otp
{ "scene": "REGISTER", "targetType": "PHONE", "target": "08123456789", "deviceId": "d-1" }
```
手机号会自动规范化成 E.164(印尼本地 `08xx` → `+62xxx`)。频控:同号 60 秒一次、每日 10 次,
同 IP 每小时 20 次;连续输错 5 次该验证码作废。
注册:
```json
POST /app/auth/register
{
"targetType": "PHONE", "target": "08123456789", "code": "123456",
"password": "可选,设置后可用密码登录",
"nickname": "可选",
"deviceId": "d-1", "platform": "IOS", "appVersion": "1.0.0",
"agreementVersion": "2026-10-01", "marketingConsent": false
}
```
响应(登录、刷新同结构):
```json
{
"code": 0, "message": "ok",
"data": {
"accessToken": "...", "refreshToken": "...", "expiresIn": 7200, "tokenType": "Bearer",
"newUser": true,
"user": { "uid": "U8KQ2ZP1", "nickname": "User8KQ2ZP", "status": "normal",
"countryCode": "ID", "language": "id-ID", "currency": "IDR",
"maskedTarget": "+628****789", "vipLevel": 0, "vipExpireAt": null }
}
}
```
### 1.2 个人中心(已实现)
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/app/me` | 当前用户资料 |
| PUT | `/app/me` | 修改昵称/头像/性别/生日/语言/时区/区域 |
| GET | `/app/me/devices` | 登录设备列表 |
| GET | `/app/me/export` | 数据导出(商店与隐私法规要求) |
| POST | `/app/me/deactivate` | 申请注销,进入 15 天冷静期并立即登出全部设备 |
| POST | `/app/me/deactivate/cancel` | 冷静期内撤销注销 |
### 1.3 内容浏览(已实现)
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/app/home/feed` | 首页:轮播 + 分区,卡片带 `preview` 可播预览集 |
| GET | `/app/discover/feed` | 发现页分区 |
| GET | `/app/categories` | 分类列表 |
| GET | `/app/dramas/{id}` | 详情(含剧集列表,免费集带**已签名**播放地址) |
### 1.4 播放(已实现,链路已重做)
| 方法 | 路径 | 鉴权 | 说明 |
| --- | --- | --- | --- |
| GET | `/app/episodes/{id}/play` | 可选 | 签发播放票据与签名地址 |
```json
{
"code": 0, "message": "ok",
"data": {
"episodeId": 12, "dramaId": 5, "number": 1, "title": "第 1 集", "durationSec": 180,
"accessible": true,
"accessType": "FREE",
"playUrl": "/files/episode/202610/xxx.mp4?e=1760000000&s=9f2c...",
"qualities": ["AUTO"],
"unlockCoin": 0,
"previewSeconds": 0
}
}
```
`accessType` 取值:`FREE`(免费集)、`ENTITLED`(已购权益)、`COIN_REQUIRED`(需解锁,
此时 `playUrl` 为空、`unlockCoin` 有值)、`NO_MEDIA`(还没上传片源或未转码完成)。
**判定顺序由服务端决定,客户端不得自行推断可否播放。** 签名默认 15 分钟有效;
开启 `fruitshorts.media.signature-required` 后,`/files/episode/**` 没带有效签名会返回 403。
### 1.5 观看记录与收藏(已实现)
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| POST | `/app/watch/progress` | 上报进度(建议每 10s 一次,切集时补一次) |
| GET | `/app/watch/progress?dramaId=` | 某剧续看位置 |
| GET | `/app/watch/recent?limit=20` | 最近观看 |
| DELETE | `/app/watch/progress/{dramaId}` | 删除某剧记录 |
| DELETE | `/app/watch/progress` | 清空记录 |
| POST | `/app/favorites/toggle` | 收藏/取消收藏,返回切换后状态 |
| GET | `/app/favorites` | 收藏列表(卡片结构与首页一致) |
| DELETE | `/app/favorites/{dramaId}` | 取消收藏 |
进度合并规则:同一集内只前进不后退;换集直接采纳新集;`finished` 只在接近片尾或客户端显式上报时置位。
这样用户换设备不会看到进度倒退。
### 1.6 离线下载(已实现)
服务端只发**授权**与**短时效地址**,分片由客户端直接向 CDN 拉取 ——
这样并发下载不会打到应用服务器,服务端只记账(进度、配额、有效期)。
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/app/downloads/policy` | 策略与配额:单设备上限、授权天数、仅 Wi-Fi、清晰度上限、已用/剩余 |
| PUT | `/app/downloads/settings` | 偏好设置:仅 Wi-Fi 下载、最高清晰度 |
| POST | `/app/downloads` | 申请下载授权(同剧集重复申请幂等,只刷新有效期) |
| GET | `/app/downloads` | 下载列表(授权 + 任务进度,可直接渲染) |
| GET | `/app/downloads/{episodeId}/url` | 断点续传前重新取地址(授权不变,地址是短时效的) |
| POST | `/app/downloads/{episodeId}/progress` | 上报下载进度,用于断点续传与跨端展示 |
| DELETE | `/app/downloads/{episodeId}` | 删除单条下载(吊销授权) |
| DELETE | `/app/downloads` | 清空当前设备全部下载,返回清除条数 |
设备标识优先取请求体 `deviceId`,其次取请求头 `X-Device-Id`。
授权默认 30 天有效,过期由每日定时任务置为 `EXPIRED`;被吊销或过期的授权客户端下次联网即失效。
需要解锁的剧集走权益判定,未解锁不允许下载(当前内容均为免费集,付费链路见 09)。
配额与有效期由 `fruitshorts.download.*` 配置,通过 `policy` 下发,客户端不要写死。
### 1.7 互动、交易、创作者(下一批)
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| POST | `/app/comments` | 发表评论(先审后发) |
| GET | `/app/comments?dramaId=&cursor=` | 评论列表(游标分页) |
| DELETE | `/app/comments/{id}` | 删除自己的评论 |
| POST | `/app/likes/toggle` | 点赞(DRAMA/EPISODE/COMMENT) |
| POST | `/app/reports` | 举报 |
| POST | `/app/orders/verify` | 提交 IAP / Google Play 票据,服务端二次校验后发权益 |
| GET | `/app/orders` | 我的订单 |
| GET | `/app/wallet` | 金币余额与流水 |
| POST | `/app/episodes/{id}/unlock` | 金币解锁分集 |
| POST | `/app/creator/apply` | 创作者入驻 |
| GET | `/app/creator/works` | 我的作品与审核状态 |
| GET | `/app/creator/stats` | 创作者数据看板 |
| GET | `/app/config` | 启动配置(协议版本、开关、最低版本) |
| GET | `/app/notices` | 系统公告 |
## 2. 后台管理接口(/admin/**)
### 2.1 认证与系统(已实现)
`POST /admin/auth/login`、`GET /admin/auth/menus`、`GET /admin/auth/profile`、
`POST /admin/auth/logout`、`POST /admin/auth/password`;
管理员、角色、权限、菜单、角色分类、系统配置、操作日志各自一套增删改查。
### 2.2 内容与媒资(已实现)
短剧(含是否热门、审核发布状态机)、剧集、分类、轮播图;媒资分片上传(init/chunk/complete)。
新增字段:短剧 `rating`(内容分级)、`regionCodes`(可播区域)、版权窗口、
`creatorId`、`tags`、`trailerUrl`;剧集 `transcodeStatus`、`hlsUrl`、`previewSeconds`、字幕。
### 2.3 App 用户管理(已实现)
| 方法 | 路径 | 权限点 | 说明 |
| --- | --- | --- | --- |
| GET | `/admin/app-users/page` | `app-user:list` | 检索:keyword(uid/昵称/手机号/邮箱)、status、countryCode、registerSource |
| GET | `/admin/app-users/{id}` | `app-user:list` | 详情 |
| GET | `/admin/app-users/{id}/ban-records` | `app-user:list` | 封禁与申诉记录 |
| PUT | `/admin/app-users/{id}/status` | `app-user:update` | 改状态:normal/limited/banned |
状态处置会**立即吊销该用户全部会话**并写入封禁记录,避免"封了还能用"。
### 2.4 下载管理(已实现)
| 方法 | 路径 | 权限点 | 说明 |
| --- | --- | --- | --- |
| GET | `/admin/downloads/page` | `download:list` | 按用户 / 设备 / 状态检索下载授权,含剧名与集号 |
| GET | `/admin/downloads/stats` | `download:list` | 授权与任务的分布统计 |
| PUT | `/admin/downloads/{id}/revoke` | `download:revoke` | 吊销授权,用户侧下次联网即失效 |
| POST | `/admin/downloads/cleanup` | `download:revoke` | 手动清理过期授权(默认每天凌晨自动执行) |
### 2.5 内容治理、订单、创作者审核、数据看板(下一批)
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| GET | `/admin/comments/page` | 评论审核队列 |
| PUT | `/admin/comments/{id}/audit` | 通过/驳回(带原因码) |
| GET | `/admin/reports/page` | 举报工单 |
| PUT | `/admin/reports/{id}/handle` | 处理举报(可联动封禁) |
| GET/POST/PUT | `/admin/sensitive-words` | 敏感词库 |
| GET | `/admin/orders/page` | 订单检索(可按用户、状态、平台) |
| POST | `/admin/orders/{orderNo}/compensate` | 票据丢失的补发 |
| PUT | `/admin/entitlements/{id}/revoke` | 权益回收 |
| POST | `/admin/reconcile/run` | 手动触发对账 |
| GET | `/admin/creators/page` | 创作者审核队列 |
| PUT | `/admin/creators/{id}/audit` | 入驻审核 |
| GET | `/admin/submissions/page` | 内容提交审核 |
| PUT | `/admin/submissions/{id}/audit` | 内容审核(通过/驳回/驳回原因码) |
| PUT | `/admin/prices` | 分集价格与免费集配置 |
| GET | `/admin/dashboard/overview` | 经营大盘(用户/播放/收入/成本) |
| GET | `/admin/dashboard/transcode` | 转码队列与失败任务 |
| POST | `/admin/notices` | 发布系统公告 |
## 3. 契约与实现状态对照
| 模块 | 表结构 | 接口 | 代码 |
| --- | --- | --- | --- |
| 01 基础与交付准备 | 已就绪 | 约定已冻结 | 脚手架 / 契约 / 签名 / 转码骨架 已实现 |
| 02 用户账号与登录 | 已建 | 已冻结 | **已实现**(手机号/邮箱,第三方登录走同一凭证表) |
| 03 内容发现与详情 | 已建 | 已冻结 | 已实现(补了分级、区域、版权、标签字段) |
| 04 播放器与播放控制 | 已建 | 已冻结 | 票据 + 签名 + 进度 已实现;多码率依赖转码 worker |
| 05 记录、收藏与下载 | 已建 | 已冻结 | **已实现**:进度、观看流水、收藏、离线下载授权与任务 |
| 06 评论、点赞与分享 | 已建 | 已冻结 | 下一批 |
| 07 创作者中心 | 已建 | 已冻结 | 下一批(转码任务与产物记账已实现) |
| 08 平台运营后台 | 已建 | 用户管理已实现 | 治理/订单/看板 下一批 |
| 09 分集收费与支付 | 已建 | 已冻结 | 下一批(权益判定端口已预留,接上即生效) |
| 10 质量、数据与上线 | 已建 | 已冻结 | 压测与埋点 下一批 |