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 仍按普通表定义唯一索引, 表分区化之后重跑脚本必定报错(分区表的唯一索引必须包含分区键)。改为在本脚本里就 按分区表定义,并验证脚本可重复执行。
This commit is contained in:
1 parent
a4aa749cc6
commit
b3179fc836
251 files changed
+7261
-767
No files matched your search
@@ -0,0 +1,264 @@
|
||||
# 第一阶段接口清单(后台管理 + 客户端)
|
||||
|
||||
> 版本 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 质量、数据与上线 | 已建 | 已冻结 | 压测与埋点 下一批 |
|
||||
+23
-23
@@ -1,4 +1,4 @@
|
||||
# ReelShort 短剧服务 · 后端架构设计
|
||||
# FruitShorts 短剧服务 · 后端架构设计
|
||||
|
||||
> 技术栈:JDK 21(LTS,编译目标 Java 17)+ Spring Boot 3.2 + PostgreSQL 18 + Redis
|
||||
>
|
||||
@@ -94,26 +94,26 @@
|
||||
```
|
||||
java/
|
||||
├── pom.xml # 聚合父 POM,统一依赖版本
|
||||
├── reelshort-common/ # 统一响应/异常/常量/枚举/工具
|
||||
├── reelshort-framework/ # 安全、MyBatis、Redis、日志、限流、OSS、MQ 客户端
|
||||
├── reelshort-module-system/ # 管理员、角色、权限、菜单、配置、操作日志
|
||||
├── reelshort-module-content/ # 短剧、剧集、分类标签、审核发布
|
||||
├── reelshort-module-media/ # 媒资上传、转码、播放地址签名
|
||||
├── reelshort-module-member/ # C 端用户、登录、设备、资料
|
||||
├── reelshort-module-interaction/ # 收藏、点赞、评论、观看历史
|
||||
├── reelshort-module-trade/ # 金币、流水、解锁、订单、VIP
|
||||
├── reelshort-module-stat/ # 数据看板、日聚合
|
||||
├── reelshort-admin-api/ # B 端接口,/admin/**
|
||||
├── reelshort-app-api/ # C 端接口,/app/**
|
||||
└── reelshort-job/ # 定时任务与异步消费者
|
||||
├── fruitshorts-common/ # 统一响应/异常/常量/枚举/工具
|
||||
├── fruitshorts-framework/ # 安全、MyBatis、Redis、日志、限流、OSS、MQ 客户端
|
||||
├── fruitshorts-module-system/ # 管理员、角色、权限、菜单、配置、操作日志
|
||||
├── fruitshorts-module-content/ # 短剧、剧集、分类标签、审核发布
|
||||
├── fruitshorts-module-media/ # 媒资上传、转码、播放地址签名
|
||||
├── fruitshorts-module-member/ # C 端用户、登录、设备、资料
|
||||
├── fruitshorts-module-interaction/ # 收藏、点赞、评论、观看历史
|
||||
├── fruitshorts-module-trade/ # 金币、流水、解锁、订单、VIP
|
||||
├── fruitshorts-module-stat/ # 数据看板、日聚合
|
||||
├── fruitshorts-admin-api/ # B 端接口,/admin/**
|
||||
├── fruitshorts-app-api/ # C 端接口,/app/**
|
||||
└── fruitshorts-job/ # 定时任务与异步消费者
|
||||
```
|
||||
|
||||
起步阶段若不想铺这么开,可先合并为 `reelshort-common` + `reelshort-server`(含 system/content/media)+ `reelshort-app-api`,但包结构仍按域划分(`com.reelshort.content.*`),后续拆模块零成本。
|
||||
起步阶段若不想铺这么开,可先合并为 `fruitshorts-common` + `fruitshorts-server`(含 system/content/media)+ `fruitshorts-app-api`,但包结构仍按域划分(`com.fruitshorts.content.*`),后续拆模块零成本。
|
||||
|
||||
模块内部结构保持一致:
|
||||
|
||||
```
|
||||
com.reelshort.content
|
||||
com.fruitshorts.content
|
||||
├── controller/ # 仅 admin-api / app-api 暴露
|
||||
├── service/ # 接口 + impl
|
||||
├── dal/
|
||||
@@ -126,7 +126,7 @@ com.reelshort.content
|
||||
|
||||
命名约定:
|
||||
|
||||
- 包名 `com.reelshort.<module>.<layer>`
|
||||
- 包名 `com.fruitshorts.<module>.<layer>`
|
||||
- 实体 `Drama`、`DramaEpisode`、`AppUser`
|
||||
- 请求对象 `XxxCreateReqVO` / `XxxUpdateReqVO` / `XxxPageReqVO`,响应对象 `XxxRespVO`
|
||||
- 表名 `<域前缀>_<业务>`,如 `drama`、`drama_episode`、`sys_role`、`app_user`、`stat_drama_daily`
|
||||
@@ -769,9 +769,9 @@ POST /app/pay/callback/{channel}
|
||||
|
||||
```
|
||||
Nginx
|
||||
├── reelshort-admin-api (/admin)
|
||||
├── reelshort-app-api (/app)
|
||||
└── reelshort-job (定时任务、异步消费)
|
||||
├── fruitshorts-admin-api (/admin)
|
||||
├── fruitshorts-app-api (/app)
|
||||
└── fruitshorts-job (定时任务、异步消费)
|
||||
PostgreSQL(主从) / Redis / OSS(或 MinIO) / 转码 Worker(独立容器,可多实例水平扩容)
|
||||
```
|
||||
|
||||
@@ -849,9 +849,9 @@ server:
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:postgresql://127.0.0.1:5432/reelshort
|
||||
username: ${DB_USER:reelshort}
|
||||
password: ${DB_PASSWORD:reelshort}
|
||||
url: jdbc:postgresql://127.0.0.1:5432/fruitshorts
|
||||
username: ${DB_USER:fruitshorts}
|
||||
password: ${DB_PASSWORD:fruitshorts}
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
@@ -861,7 +861,7 @@ spring:
|
||||
max-file-size: 2GB
|
||||
max-request-size: 2GB
|
||||
|
||||
reelshort:
|
||||
fruitshorts:
|
||||
jwt:
|
||||
admin-secret: ${JWT_ADMIN_SECRET}
|
||||
app-secret: ${JWT_APP_SECRET}
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
# 后端服务设计(第一阶段 · 印尼市场 · 1 万并发观看)
|
||||
|
||||
> 版本 v1.0 · 配套 [第一阶段 PRD](./prd-phase1.md) · 现有实现见 [架构设计](./architecture-design.md)
|
||||
> 目标口径:**10,000 路并发播放不劣化**(对应 DAU 约 25–35 万),首发印尼。
|
||||
|
||||
## 1. 目标与约束
|
||||
|
||||
**业务目标**:日活规模下 1 万路并发观看流畅(首帧 P95 < 1.5s、卡顿率 < 2%),分集收费闭环准确无误,创作者上传到可播放全自动。
|
||||
|
||||
**工程约束**:
|
||||
|
||||
1. 首发印尼,用户全部在东南亚,服务端与内容必须就近部署,不能用国内机房直连。
|
||||
2. 团队是 14–16 人的 MVP 团队,运维只有 1 人,所以不做微服务拆分,保持模块化单体。
|
||||
3. 数字商品必须走 Apple IAP / Google Play Billing,服务端只做校验与权益发放。
|
||||
4. 成本敏感:视频流量是最大成本项,码率与缓存策略从一开始就要按成本设计。
|
||||
|
||||
## 2. 现状盘点与差距
|
||||
|
||||
当前实现:7 个 Maven 模块的模块化单体,`fruitshorts-admin-api`(8080) + `fruitshorts-app-api`(8081),
|
||||
MyBatis-Plus + PostgreSQL,媒资落本地磁盘 `data/uploads`,静态文件直出,无转码、无 CDN、无缓存层、无消息队列。
|
||||
|
||||
| 能力 | 现状 | 目标 | 优先级 |
|
||||
| --- | --- | --- | --- |
|
||||
| 媒资存储 | 本地磁盘,随机器走 | 对象存储(S3 兼容)+ 生命周期分层 | P0 |
|
||||
| 视频分发 | 源站直出原文件 | 转码成 HLS 多码率 + CDN 分发 + 签名 URL | P0 |
|
||||
| 缓存 | 无 | Redis + 本地 Caffeine 二级缓存 | P0 |
|
||||
| 异步 | 无 | 消息队列(转码任务、计数、埋点、对账) | P0 |
|
||||
| 数据库 | 单实例单库 | 主库 + 只读副本,热点与日志表分区 | P1 |
|
||||
| 播放鉴权 | 免费集直接给地址 | 票据 + 签名 URL + 有效期 + 吊销 | P0 |
|
||||
| 限流熔断 | 无 | 网关级限流 + 按接口降级 | P0 |
|
||||
| 支付 | 未实现 | 订单状态机 + 票据校验 + 对账 | P0 |
|
||||
| 可观测 | 慢请求日志 | 指标 + 链路追踪 + 告警 + 看板 | P1 |
|
||||
|
||||
结论:现有代码是功能验证级,架构骨架(模块划分、鉴权、内容与媒资模型)可以直接复用,
|
||||
但"视频从源站直出"这一点在 1 万并发下会立刻打爆,必须替换成 CDN + 签名 URL。
|
||||
|
||||
## 3. 容量规划
|
||||
|
||||
### 3.1 码率阶梯与带宽
|
||||
|
||||
印尼以 4G 为主、资费敏感,码率阶梯压到偏低档位:
|
||||
|
||||
| 档位 | 分辨率 | 视频 | 音频 | 合计 | 预期占比 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 360p | 640x360 | 400 kbps | 64 kbps | ~0.47 Mbps | 40% |
|
||||
| 480p | 854x480 | 800 kbps | 64 kbps | ~0.87 Mbps | 45% |
|
||||
| 720p | 1280x720 | 1600 kbps | 96 kbps | ~1.70 Mbps | 12% |
|
||||
| 1080p | 1920x1080 | 3200 kbps | 128 kbps | ~3.30 Mbps | 3% |
|
||||
|
||||
按占比加权,平均码率约 **0.88 Mbps**。
|
||||
|
||||
- 10,000 路并发出口带宽约 **8.8 Gbps**
|
||||
- 叠加首段突发、重连与预加载(+30%),峰值 **约 11–12 Gbps**
|
||||
- CDN 命中率按 98% 计,回源带宽 **约 240 Mbps**
|
||||
|
||||
源站只需 2 台带 1 Gbps 出口的机器即可承载回源,不需要为每个观众准备源站带宽。
|
||||
这条推导是整个设计的关键:**播放流量必须由 CDN 承担,源站只回源 2%**。
|
||||
|
||||
### 3.2 接口 QPS
|
||||
|
||||
| 链路 | 估算方式 | 平均 | 峰值(x3,20:00–23:00 WIB) |
|
||||
| --- | --- | --- | --- |
|
||||
| 首页/发现/详情 | 每活跃用户每分钟 2 次 | ~333 QPS | ~1,000 QPS |
|
||||
| 播放票据签发 | 每集一次,平均 4 分钟/集 | ~42 QPS | ~125 QPS |
|
||||
| 播放进度上报 | 每 10 秒一次 | 1,000 QPS | 3,000 QPS |
|
||||
| 埋点/播放计数 | 客户端按 30s 合批 | ~330 QPS | ~1,000 QPS |
|
||||
|
||||
结论:
|
||||
|
||||
- **读接口** 经 Redis + 本地缓存后,4–8 个 API 副本足够(单副本可撑 1,500+ QPS)。
|
||||
- **进度上报不直接打库**:写 Redis,后台任务按 `user+drama` 合并后批量 UPSERT,落库压力降到 100 QPS 量级。
|
||||
- **埋点与计数走消息队列**,消费者聚合后写回,不用事务强一致。
|
||||
|
||||
### 3.3 存储与转码
|
||||
|
||||
按第一阶段 300 部 x 60 集 x 单集 2 分钟估算:
|
||||
|
||||
| 项 | 计算 | 容量 |
|
||||
| --- | --- | --- |
|
||||
| 原片(1080p) | 18,000 集 x ~50 MB | ~0.9 TB |
|
||||
| 转码产物(4 档 HLS) | 18,000 集 x ~95 MB | ~1.7 TB |
|
||||
| 封面/截图/字幕 | — | ~0.1 TB |
|
||||
| **合计** | | **约 3 TB** |
|
||||
|
||||
转码算力:单集 1080p 转 4 档约 8 分钟机时,首月入库 18,000 集约 2,400 小时;
|
||||
100 核并行约 24 小时跑完。**按需弹性即可,不需要常驻转码集群**,但必须排队限流,避免抢在线服务的 CPU。
|
||||
|
||||
### 3.4 成本与单位经济性
|
||||
|
||||
假设 DAU 30 万、人均日观看 40 分钟、平均码率 0.88 Mbps:
|
||||
|
||||
| 项 | 月度量/成本 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| CDN 流量 | 约 79 TB/天,约 2,300 TB/月,**$50k–75k** | 按东南亚 $0.022–0.032/GB,**最大成本项** |
|
||||
| 对象存储 | 3 TB,约 $60–100 | 冷热分层后更低 |
|
||||
| 数据库 / Redis / MQ | $300–800 | 主从 + 只读副本 + 集群缓存 |
|
||||
| API 与后台计算 | $400–1,200 | 4–8 副本 + 弹性转码 |
|
||||
| 短信 OTP | $500–2,000 | 按 $0.02–0.08/条,必须频控 |
|
||||
| 监控 / 日志 / APM | $100–300 | |
|
||||
| **合计** | **约 $52k–79k/月** | CDN 占绝大部分可变成本 |
|
||||
|
||||
单位经济性(决定商业模型是否成立):
|
||||
|
||||
- 单集 2 分钟、0.88 Mbps,约 13 MB 流量,CDN 成本约 **$0.0004**
|
||||
- 单集解锁(按 30 金币约 $0.03),视频成本约占解锁收入 **1.5%**
|
||||
|
||||
推论:
|
||||
|
||||
1. 金币解锁模式下视频成本可忽略;真正的压力来自第二阶段的广告模式(ARPU 低一个数量级时 CDN 会变成主要成本)。
|
||||
2. 因此第一阶段就要把平均码率、缓存命中率、CDN 单价做成**看板与告警**(单位播放成本 $/千次播放)。
|
||||
3. 把平均码率从 0.88 压到 0.6 Mbps(更多 360p、更激进的 ABR 起播策略)可直接省掉约 30% 的 CDN 账单,比任何架构优化都有效。
|
||||
|
||||
## 4. 部署与网络拓扑
|
||||
|
||||
```
|
||||
印尼用户 (iOS / Android)
|
||||
| 1. 接口请求(JSON, HTTPS)
|
||||
v
|
||||
CDN / 全球加速 --- 2. 视频分片与静态资源(HLS,签名 URL,命中率 >=98%)
|
||||
| ^
|
||||
| 回源(约 2%) |
|
||||
v |
|
||||
区域入口 (Jakarta) |
|
||||
|- API 网关:TLS、限流、鉴权、WAF |
|
||||
|- app-api x4-8(K8s HPA) |
|
||||
|- admin-api x2(可放新加坡/内网,仅内部使用) |
|
||||
|- worker(转码调度、计数聚合、对账、清理) |
|
||||
|
|
||||
|- PostgreSQL(主 + 只读副本)
|
||||
|- Redis(集群:缓存 / 票据 / 进度缓冲 / 限流)
|
||||
|- 消息队列(转码任务、埋点、计数、对账事件)
|
||||
|- 对象存储 OSS(S3 兼容)--- 转码产物 -----------+
|
||||
```
|
||||
|
||||
**区域选择**:首选**雅加达**(阿里云 / 腾讯云 / AWS 均有区域),退路是新加坡
|
||||
(ap-southeast-1,到雅加达 RTT 约 20–30ms,仍可接受)。依据是到用户 RTT、CDN 覆盖与本地 PoP 价格、数据驻留要求。
|
||||
|
||||
**为什么要 API 网关**:限流、IP 黑名单、WAF、灰度都放在网关层,业务服务不必各自实现一遍。
|
||||
|
||||
**国内团队访问生产**:运营后台走独立入口 + 内网跳板,不要与 C 端出口混用。
|
||||
|
||||
## 5. 服务划分与演进
|
||||
|
||||
第一阶段**保持模块化单体**,按业务域划清边界,为后续拆分留缝:
|
||||
|
||||
| 模块 | 职责 | 未来拆分优先级 |
|
||||
| --- | --- | --- |
|
||||
| user | 账号、凭证、会话、资料、风控 | 中 |
|
||||
| content | 短剧、剧集、分类、标签、榜单 | 低 |
|
||||
| media | 上传、转码、媒资、播放票据 | **高**(与 CDN/转码强绑定) |
|
||||
| playback | 进度、记录、收藏、下载授权 | 中 |
|
||||
| interaction | 评论、点赞、举报、分享 | 低 |
|
||||
| commerce | 商品、订单、钱包、权益、对账 | **高**(支付需审计隔离) |
|
||||
| creator | 创作者入驻、内容提交、数据看板 | 中 |
|
||||
| admin/system | RBAC、菜单、审计、配置 | 低 |
|
||||
|
||||
拆分只在这两个条件下发生:**独立扩缩容需求**或**合规隔离需求**。其余情况一律不加服务。
|
||||
|
||||
## 6. 数据模型与存储
|
||||
|
||||
核心表(现有 `sql/schema.sql` 已有基础,"新增"为需要补充的部分):
|
||||
|
||||
- 内容域:`drama`、`drama_episode`、`drama_category`、`content_banner`,已具备。
|
||||
- 媒资域:`media_asset`、`media_upload_task` 已具备;**新增** `transcode_task`、`media_rendition`(多码率产物)。
|
||||
- 用户域:**新增** `app_user`、`user_credential`、`user_session`、`user_profile`(当前只有后台管理员表)。
|
||||
- 播放域:**新增** `watch_progress`(user+drama 唯一)、`watch_history`(按月分区)、`user_favorite`、`playback_ticket`。
|
||||
- 互动域:**新增** `comment`、`comment_reply`、`user_like`、`report`、`blocklist`。
|
||||
- 交易域:**新增** `product`、`order`、`order_event`、`wallet`、`coin_ledger`、`entitlement`、`receipt`。
|
||||
- 创作者域:**新增** `creator`、`creator_document`、`content_submission`、`audit_record`。
|
||||
|
||||
存储策略:
|
||||
|
||||
- 大表分区:`watch_history`、`order_event`、`coin_ledger`、`playback_event` 按月分区,保留 12–24 个月。
|
||||
- 冷热分离:长期不播放的媒资转低频/归档存储,源站只保留热内容。
|
||||
- 读写分离:报表、导出、后台检索走只读副本,避免影响 C 端。
|
||||
- 连接池:Hikari 每副本 20–30 连接,按副本数 x 连接数留足 PG `max_connections` 余量。
|
||||
|
||||
## 7. 关键链路设计
|
||||
|
||||
### 7.1 播放与地址签发(第一阶段最重要的改造)
|
||||
|
||||
1. 客户端请求 `/app/episodes/{id}/play`,服务端判定权益(已购 > 活动免费 > 免费集 > 试看 > 金币可扣 > 需充值)。
|
||||
2. 可播放时签发**短时效签名 URL**(建议 15 分钟),签名绑定 `episodeId + userId + 清晰度 + 过期时间`。
|
||||
3. 返回 HLS master playlist 地址(CDN 域名),CDN 校验签名后才回源。
|
||||
4. 签名校验失败、过期或用户被吊销,直接 403,客户端提示重新获取。
|
||||
5. 防盗链:Referer/UA 白名单 + 单账号同时在播设备数限制(建议 1–2 台)。
|
||||
|
||||
关键点:客户端拿不到可长期复用的裸地址,也不会出现"服务端扛视频"的情况。
|
||||
|
||||
### 7.2 上传与转码流水线
|
||||
|
||||
```
|
||||
创作者分片直传 OSS(STS 临时凭证)
|
||||
-> 合并校验(MD5 / 时长 / 编码探测)
|
||||
-> 写 transcode_task(排队)
|
||||
-> worker 拉起 FFmpeg 或云转码:4 档 HLS + 封面 + 字幕封装
|
||||
-> 回调写 media_rendition
|
||||
-> CDN 预热
|
||||
-> 状态回写创作者中心(可发布)
|
||||
```
|
||||
|
||||
失败重试 3 次并记录原因;转码队列与在线服务隔离(独立节点池或命名空间),避免抢占资源。
|
||||
|
||||
### 7.3 支付与权益
|
||||
|
||||
1. 客户端拿到商店票据后提交 `/app/orders/verify`。
|
||||
2. 服务端**必须**再调 Apple / Google 校验接口二次验证,不信任客户端上报。
|
||||
3. 订单状态机幂等:以 `(platform, transactionId)` 唯一键去重,重复回调只返回既有结果。
|
||||
4. 权益发放与订单在同一事务;发放失败进补偿队列,T+1 内自动修复并留痕。
|
||||
5. 退款与撤销事件(App Store Server Notifications / Google RTDN)触发权益回收或欠款标记,写 `coin_ledger`。
|
||||
6. 每日对账:拉取商店账单与本地订单比对,差异进入人工处理队列并告警。
|
||||
|
||||
### 7.4 观看进度与记录
|
||||
|
||||
- 客户端每 10s 上报一次;服务端写 Redis(`progress:{user}:{drama}`),不直接写库。
|
||||
- 后台任务每 30s 把 Redis 中的变更按剧合并,批量 UPSERT 到 `watch_progress`(每用户每剧一行)。
|
||||
- `watch_history` 按事件追加、按月分区,用于"最近观看"与后续推荐。
|
||||
- 冲突规则:以 `updatedAt` 较新者为准;同秒冲突取进度较大者,避免进度倒退。
|
||||
|
||||
### 7.5 计数与埋点
|
||||
|
||||
播放数、完播数、点赞数一律走消息队列,消费者按窗口聚合后定时写回计数表。
|
||||
读取计数走缓存,允许秒级最终一致,不允许为了强一致把写压力压到主库。
|
||||
|
||||
## 8. 高并发与稳定性手段
|
||||
|
||||
| 手段 | 落点 |
|
||||
| --- | --- |
|
||||
| 多级缓存 | 客户端本地缓存 > CDN > Redis > Caffeine 本地缓存 > PG |
|
||||
| 读缓存 | 首页/详情/分类结果缓存 30–300s,运营改动通过版本号主动失效 |
|
||||
| 异步化 | 进度、计数、埋点、转码、对账全部异步 |
|
||||
| 合批 | 进度上报合并写、计数聚合写、日志批量落盘 |
|
||||
| 限流 | 网关按 IP/设备/用户三维限流,重点保护 OTP 与票据签发 |
|
||||
| 熔断降级 | 短信通道、商店校验、审核供应商失败时降级并告警;播放链路的降级是"降码率"而不是"不可播" |
|
||||
| 幂等 | 订单、回调、注册、上传合并全部幂等键化 |
|
||||
| 游标分页 | 列表接口用 cursor 而非深 offset,避免大偏移查询 |
|
||||
| 连接管理 | 视频分片走 CDN,API 走 HTTP/2 keep-alive,PG 连接池按副本配额 |
|
||||
| 压测门禁 | 上线前通过 1 万并发播放 + 1,000 QPS 接口压测,错误率 < 0.1% |
|
||||
|
||||
## 9. 可观测性与 SLO
|
||||
|
||||
| 指标 | 目标 | 告警阈值 |
|
||||
| --- | --- | --- |
|
||||
| 接口可用性 | 99.9% | 5 分钟错误率 > 1% |
|
||||
| 接口延迟 P95(客户端热点接口) | < 50ms | > 100ms 持续 5 分钟 |
|
||||
| 首帧 P95 | < 1.5s | > 2.5s |
|
||||
| 卡顿率 | < 2% | > 4% |
|
||||
| CDN 命中率 | >= 98% | < 95% |
|
||||
| 单位播放成本 | 有基线并有月度预算 | 超预算 20% |
|
||||
| 支付成功率 | > 97% | < 95% |
|
||||
|
||||
埋点与看板分四层:接口层(QPS/延迟/错误码)、播放层(起播/卡顿/完播/ERROR_CODE)、
|
||||
业务层(注册/首播/解锁/支付)、成本层(CDN 流量/存储/短信条数)。
|
||||
链路追踪覆盖"支付"与"播放票据签发"两条关键链路。
|
||||
|
||||
## 10. 安全与合规(印尼)
|
||||
|
||||
- **合规**:Kominfo **PSE 注册**(未注册可能被整体封锁);UU 27/2022 个人数据保护(明示同意、删除权、导出权、跨境传输依据);内容分级(R13/R17/R21)与色情、宗教、政治敏感内容零容忍。
|
||||
- **应用安全**:网关 WAF;接口越权(水平 + 垂直)全量校验;密钥集中托管并支持轮换;后台敏感操作二次确认 + 审计留痕。
|
||||
- **支付安全**:服务端票据二次校验;防重放(nonce + 唯一键);金额与商品以服务端配置为准。
|
||||
- **内容保护**:签名 URL + 短时效 + 并发设备限制;第一阶段不上 DRM,但 URL 结构与接口要为第四阶段 DRM 预留。
|
||||
- **数据分级**:手机号、邮箱、设备标识、支付标识分级存储与脱敏;日志与埋点禁止写入 PII 明文。
|
||||
|
||||
## 11. 上线与扩容路径
|
||||
|
||||
1. **环境**:dev / test / staging / prod 四套,配置外置,密钥不入库不入 Git。
|
||||
2. **压测**:阶梯加压到 1 万并发播放(走真实 CDN 链路)+ 1,000 QPS 接口,记录水位与瓶颈。
|
||||
3. **灰度**:先 5% 流量,观察起播、卡顿、错误率与支付成功率,再逐步放量。
|
||||
4. **回滚**:镜像与配置双版本共存,一键回滚;数据库变更必须向后兼容(先加字段、再切读写、最后清理)。
|
||||
5. **扩容触发线**:API 副本 CPU > 60% 或 QPS 超过设计值 70%;转码队列积压 > 30 分钟;CDN 命中率 < 95%。
|
||||
6. **灾难恢复**:数据库 PITR + 每日全量备份(跨可用区),目标 RPO <= 15 分钟、RTO <= 2 小时,季度演练一次。
|
||||
|
||||
## 12. 与第一阶段工作包的对应关系
|
||||
|
||||
| 评估表模块 | 后端承担的部分 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| 01 基础与交付准备 | 服务骨架、OpenAPI 契约、ERD、CI/CD、环境与监控 | P0 |
|
||||
| 02 用户账号与登录 | 用户域、OTP 与风控、会话与令牌、注销与导出 | P0 |
|
||||
| 03 内容发现与详情 | 内容目录、搜索、分类榜单、可见性规则、缓存 | P0 |
|
||||
| 04 播放器与播放控制 | **票据与签名 URL、转码与 ABR 片源、进度与续播接口、播放事件** | P0 |
|
||||
| 05 记录、收藏与下载 | 进度存储与合并、收藏服务、下载授权与设备绑定 | P0 |
|
||||
| 06 评论、点赞与分享 | 点赞与评论服务、内容治理联动、分享落地页接口 | P1 |
|
||||
| 07 创作者中心 | 入驻与审核流、分片上传、转码流水线、媒资与状态服务 | P0 |
|
||||
| 08 平台运营后台 | RBAC 与审计、内容/用户/订单管理、大盘数据接口 | P0 |
|
||||
| 09 分集收费与支付 | 钱包与订单、票据校验、权益决策、退款与对账 | P0 |
|
||||
| 10 质量、数据与上线 | 压测与容量报告、安全测试、埋点验收、发布与 DR Runbook | P0 |
|
||||
|
||||
建议的开发顺序(后端视角):
|
||||
基础骨架与契约 → 账号与鉴权 → 内容目录与缓存 → 媒资上传与转码 → 票据与签名播放 →
|
||||
进度与收藏 → 支付与权益 → 运营后台与治理 → 压测与上线准备。
|
||||
|
||||
其中"票据 + 签名 URL + 转码"是第一阶段风险最高、最不该压缩的链路。
|
||||
@@ -0,0 +1,272 @@
|
||||
# 短剧 App 第一阶段 PRD(印尼首发)
|
||||
|
||||
> 版本 v1.0 · 依据《四阶段功能规划与工作量评估》第一阶段明细(10 个模块 / 614 净人天)
|
||||
> 配套文档:[后端服务设计](./backend-design-phase1.md) · [架构设计](./architecture-design.md)
|
||||
|
||||
## 0. 文档说明
|
||||
|
||||
本 PRD 覆盖第一阶段(M0–M5)的产品范围,即"可上线、可收费、可运营、可供给内容"的 MVP 闭环。
|
||||
第二至第四阶段(会员订阅、广告、个性化推荐、多区域部署、DRM)不在本文范围内,仅在衔接处标注预留。
|
||||
|
||||
阅读约定:`P0` 为上线阻塞项,`P1` 为可延后但不能砍项;"人天"沿用评估表的合并口径,不重复计入通用后台与基础服务。
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 1.1 背景
|
||||
|
||||
短剧(竖屏、单集 1–3 分钟、强钩子、连续解锁)在国内已被验证。本项目把它做到海外,首发印尼:
|
||||
人口 2.8 亿、移动端占比极高、年轻用户对短视频形态接受度高,但支付能力分散、网络质量参差、
|
||||
内容与宗教文化敏感度高,因此不能直接照搬国内产品与运营方式。
|
||||
|
||||
### 1.2 第一阶段目标
|
||||
|
||||
做出一个能真实跑起来的业务闭环,而不是功能演示:
|
||||
|
||||
1. **能看**:iOS / Android 双端可安装,播放稳定,弱网可用。
|
||||
2. **能收**:分集收费闭环走通,且完全符合 Apple / Google 的数字商品结算规则。
|
||||
3. **能供给**:创作者能自助入驻、上传、提交审核、看到数据。
|
||||
4. **能运营**:运营能编排内容、处理审核与举报、查订单与用户、看经营大盘。
|
||||
5. **能被度量**:埋点口径统一,核心指标可对账、可归因。
|
||||
|
||||
### 1.3 阶段退出标准
|
||||
|
||||
- 通过 UAT 与安全测试,高危漏洞清零;
|
||||
- 支付、播放、内容上下架、账号注销四条全链路端到端通过;
|
||||
- 商店审核材料齐备且首次提审材料可完整复现核心流程。
|
||||
|
||||
## 2. 市场与假设(M0 决策门禁)
|
||||
|
||||
以下四项未冻结前不启动研发,否则登录、支付、版权、短信、UI 与商店资料会反复返工。
|
||||
|
||||
| 决策项 | 当前假设 | 影响面 |
|
||||
| --- | --- | --- |
|
||||
| 首发市场 | 印度尼西亚 | 支付税制、短信到达率、隐私法、内容分级 |
|
||||
| 首发语言 | 印尼语(Bahasa Indonesia)为主,界面预留英文 | 文案长度、字幕、客服、ASO |
|
||||
| 内容形态 | 竖屏短剧,单集 1–3 分钟,单剧 40–80 集 | 转码阶梯、CDN 成本、存储、审核成本 |
|
||||
| 收费方式 | 金币解锁分集,走 Apple IAP / Google Play Billing | 财务模型(15%–30% 抽成 + 税费)、审核 |
|
||||
|
||||
### 2.1 印尼本地化的硬性要求
|
||||
|
||||
- **PSE 注册**:印尼通信与信息部(Kominfo)要求电子系统运营商完成 PSE 注册,未注册可能被封锁。立项即启动。
|
||||
- **数据保护**:印尼《个人数据保护法》(UU 27/2022)要求明示同意、可删除、可导出,跨境传输需合法依据。
|
||||
- **内容分级与合规**:需遵守印尼内容分级(R13/R17/R21 等)与色情、宗教、政治敏感内容的严格限制;上线前需完成内容审核供应商与规则配置。
|
||||
- **商店支付**:App 内数字内容必须走商店结算,任何引导外部支付的文案都会被拒审或下架。
|
||||
- **网络与资费**:4G 为主、数据资费敏感、部分区域带宽受限 → 默认低码率起播,清晰度手工可上调。
|
||||
- **时区与货币**:WIB(UTC+7),IDR 展示按千位分隔(`Rp 12.000`);所有时间字段按用户本地时区展示。
|
||||
|
||||
## 3. 用户与角色
|
||||
|
||||
| 角色 | 主要场景 | 关键诉求 |
|
||||
| --- | --- | --- |
|
||||
| 观众(C 端) | 找剧、刷剧、追更、解锁分集 | 起播快、不卡、剧情钩子密、价格透明 |
|
||||
| 创作者 | 入驻、上传、提交审核、看数据 | 上传稳定、审核反馈明确、数据可信 |
|
||||
| 内容审核 | 审核创作者内容与片源 | 批量高效、驳回原因可复用、留痕 |
|
||||
| 运营 | 编排内容、配置定价与活动、看大盘 | 改动即时生效、可回滚 |
|
||||
| 客服 | 处理退款、账号、播放问题 | 按用户/订单快速检索,有处理入口 |
|
||||
| 财务 | 对账、退款、异常订单 | 订单与商店账单可对账,差异可追溯 |
|
||||
| 管理员 | 账号与权限管理 | RBAC 清晰、敏感操作留痕 |
|
||||
|
||||
## 4. 范围
|
||||
|
||||
### 4.1 纳入(第一阶段)
|
||||
|
||||
| 模块 | 人天 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| 01 基础与交付准备 | 8 | P0 |
|
||||
| 02 用户账号与登录 | 35 | P0 |
|
||||
| 03 内容发现与详情 | 37 | P0 |
|
||||
| 04 播放器与播放控制 | 44 | P0 |
|
||||
| 05 记录、收藏与下载 | 71 | P0 |
|
||||
| 06 评论、点赞与分享 | 64 | P1 |
|
||||
| 07 创作者中心 | 119 | P0 |
|
||||
| 08 平台运营后台 | 100 | P0 |
|
||||
| 09 分集收费与支付 | 98 | P0 |
|
||||
| 10 质量、数据与上线 | 38 | P0 |
|
||||
|
||||
### 4.2 明确不做(留到后续阶段)
|
||||
|
||||
个性化推荐模型、会员订阅与自动续费、广告插入、创作者分成结算、多语言配音、DRM、多区域容灾、TV/Web 观看端。
|
||||
|
||||
## 5. 功能需求
|
||||
|
||||
每个模块给出:需求点 → 验收要点。验收要点直接作为 QA 的测试设计输入。
|
||||
|
||||
### 5.1 基础与交付准备(8 人天)
|
||||
|
||||
需求点:需求澄清与范围冻结;技术架构、API 契约与数据模型;双端与 Web 工程骨架;后端服务骨架、鉴权与基础中间件;
|
||||
对象存储与转码链路;短信/邮件/推送通道;崩溃与性能监控;埋点规范与数据字典;CI/CD、多环境、域名与 CDN;商店账号与签名;隐私与支付前置。
|
||||
|
||||
验收要点:契约兼容与越权校验;构建产物可复现;上传转码回调闭环;多国家短信到达与限流;事件去重与口径一致。
|
||||
|
||||
### 5.2 用户账号与登录(35 人天)
|
||||
|
||||
需求点:手机号 + 邮箱 + Apple / Google 三种登录;多国家区号与 OTP 风控;隐私同意与年龄门槛;Token 与多设备会话;
|
||||
资料(昵称/头像/地区);账号注销与数据导出;封禁、限制与申诉;登录注册限流反刷。
|
||||
|
||||
验收要点:
|
||||
|
||||
- 并发注册幂等,重复提交不产生重复账号;
|
||||
- 验证码刷取被拦截(同号/同 IP/同设备多维频控 + 图形验证码 + 熔断);
|
||||
- 跨端登录状态一致,登出后旧 Token 立即失效;
|
||||
- Sign in with Apple 提供"隐藏邮箱"路径下的账号可正常续用;
|
||||
- 注销后 15 天内数据不可恢复,导出内容完整且不含他人数据。
|
||||
|
||||
### 5.3 内容发现与详情(37 人天)
|
||||
|
||||
需求点:首页浏览流;关键词搜索(含热词与历史);分类与榜单;详情与选集;规则推荐与热度排序;地区/年龄/状态/版权过滤;加载、空态、离线与错误重试。
|
||||
|
||||
验收要点:上下架与地区过滤实时生效;分页刷新不错位不重复;深链进入返回栈正确;空结果有兜底;弱网超时不空白。
|
||||
|
||||
### 5.4 播放器与播放控制(44 人天)
|
||||
|
||||
需求点:HLS 播放与生命周期管理;播放地址签发与试看校验;快进快退与手势;0.75x–2.0x 倍速与记忆;全屏/旋转/锁屏;
|
||||
断点续播与异常恢复;自适应码率与清晰度切换;基础字幕;缓冲/断网/错误码提示;自动下一集;音频焦点与系统媒体控制;播放事件埋点。
|
||||
|
||||
验收要点(本模块是全阶段最影响口碑的一环):
|
||||
|
||||
- 首帧 P95 < 1.5s,卡顿率 < 2%,弱网(3G / 丢包 5%)可降级播放不黑屏;
|
||||
- 播放地址不可复用:签名过期后直接播放失败,越权拼接地址拿不到片源;
|
||||
- 切集不重复计数,进度不跳变;
|
||||
- 全程 2 小时连续播放无内存增长与音画不同步。
|
||||
|
||||
### 5.5 记录、收藏与下载(71 人天)
|
||||
|
||||
需求点:观看记录(列表、续播、清理);收藏(状态同步、排序、批量删除);离线下载(队列、断点续传、加密存储与离线播放);
|
||||
下载授权与设备绑定、过期与吊销;存储占用管理与仅 Wi-Fi 下载;跨端同步策略;与账号注销联动。
|
||||
|
||||
验收要点:授权、过期、吊销、换机四条路径可验证;下载文件无法被外部播放器直接提取;跨端进度冲突有确定的合并规则。
|
||||
|
||||
> 产品决策待定:第一阶段是否启用"真实离线下载"。若法务与版权未就绪,先做受控缓存(App 内缓存 + 授权有效期),
|
||||
> 该决策会影响 05 模块约 20 人天的用量。
|
||||
|
||||
### 5.6 评论、点赞与分享(64 人天,P1)
|
||||
|
||||
需求点:点赞(动画、计数、防刷);评论(发布、回复、删除、分页、审核状态);敏感词、举报、拉黑与审核联动;
|
||||
系统分享、复制链接与海报;深链与落地页;分享素材模板。
|
||||
|
||||
验收要点:计数与实际关系一致(异步最终一致但可对账);审核中的评论对他人不可见;深链在未安装场景可唤起商店。
|
||||
|
||||
### 5.7 创作者中心(119 人天,最大单模块)
|
||||
|
||||
需求点:创作者入驻(身份资料、协议、审核状态);工作台与内容列表;分片上传与断点续传;上传任务校验、合并与回调;
|
||||
剧集/分集/封面/标签管理;多码率转码与媒资管理;人工审核与驳回原因;定时发布、下架、撤回与版权到期;
|
||||
免费/收费分集与试看规则;数据看板;审核结果与系统公告;角色隔离与敏感操作审计;多语言与币种展示预留。
|
||||
|
||||
验收要点:上传中断可续传且不产生重复媒资;转码失败可重试且失败原因可见;审核状态流转无"卡死"态;
|
||||
创作者只能看到自己的数据(水平越权测试覆盖);版权到期自动下架。
|
||||
|
||||
### 5.8 平台运营后台(100 人天)
|
||||
|
||||
需求点:管理员登录、RBAC、审批与操作审计;内容/剧集/分类/标签/榜单/推荐位管理;创作者资质与违规处理;
|
||||
用户检索与客服视图;评论举报与封禁申诉;订单、退款、补发与权益处理;定价与活动配置;经营大盘;批量操作与受限导出。
|
||||
|
||||
验收要点:权限变更后刷新即生效;关键操作 100% 留痕(操作人、时间、请求与响应);导出受限且可审计。
|
||||
|
||||
### 5.9 分集收费与支付(98 人天)
|
||||
|
||||
需求点:钱包与金币;订单与权益幂等状态机;商品配置与价格分层;Apple IAP 与 Google Play Billing 集成;服务端票据校验与重放防护;
|
||||
恢复购买与跨设备权益;退款、撤销、拒付与权益回收;商店账单对账;付费弹窗与解锁联动;免费集/试看/已购判断;定价实验预留。
|
||||
|
||||
验收要点:
|
||||
|
||||
- 同一票据重复回调只发放一次权益(幂等);
|
||||
- 票据丢失、异步通知延迟时,补偿任务能在 T+1 内自动修复并留痕;
|
||||
- 恢复购买在换设备、重装、切区三种场景下权益一致;
|
||||
- 每日与商店账单自动对账,差异进入人工处理队列。
|
||||
|
||||
### 5.10 质量、数据与上线(38 人天)
|
||||
|
||||
需求点:核心用例自动化与冒烟;接口契约与集成测试;登录/内容/播放/支付/后台压测;安全测试;
|
||||
IAP/Billing 沙盒到权益的端到端验证;埋点与指标一致性验证;UAT;商店素材与提审;灰度与回滚演练;值班与事故响应;备份与灾难恢复。
|
||||
|
||||
## 6. 核心流程与状态机
|
||||
|
||||
**内容状态**:草稿 → 待审核 → 审核中 →(驳回 → 草稿)→ 已通过 → 定时发布 → 已发布 → 已下架 / 版权到期。
|
||||
任何状态下都必须能被运营强制下架,且下架对已缓存列表在 60s 内生效。
|
||||
|
||||
**订单状态**:创建 → 已支付 → 已发放权益;异常分支:支付超时关闭、票据校验失败待补偿、退款中、已退款(回收权益)。
|
||||
所有状态迁移必须幂等,重复回调不改变已终态订单。
|
||||
|
||||
**权益判定优先级**:已购 > 活动免费 > 免费集 > 试看剩余 > 金币可扣 > 需充值。
|
||||
判定结果必须由服务端返回,客户端不得自行推断可播放性。
|
||||
|
||||
**账号状态**:未注册 → 已注册 → (受限 → 申诉中 → 恢复)/ 已注销。封禁与注销必须同步清理会话与缓存。
|
||||
|
||||
## 7. 非功能需求
|
||||
|
||||
### 7.1 性能与容量
|
||||
|
||||
业务方给的目标是"日 1 万加用户同时观看视频流畅"。这里按更严的口径落地:
|
||||
|
||||
| 指标 | 目标 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 并发播放路数 | ≥ 10,000 路不劣化 | 对应 DAU 约 25–35 万;详见后端设计容量推导 |
|
||||
| 首帧时间 P95 | < 1.5s | 4G 网络、360p 起播 |
|
||||
| 卡顿率 | < 2% | 播放时长加权 |
|
||||
| 接口延迟 P95 | < 100ms(客户端热点接口 < 50ms) | 服务端耗时 |
|
||||
| 可用性 | 99.9%(第一阶段) | 月度 |
|
||||
|
||||
若实际口径是"DAU 1 万",资源需求下降约一个数量级,可先按小规格部署、保留水平扩容能力。
|
||||
|
||||
### 7.2 安全
|
||||
|
||||
播放地址签名与防盗链;接口越权校验(水平 + 垂直);支付服务端校验与重放防护;后台敏感操作二次确认与留痕;
|
||||
密钥集中托管与轮换;上线前渗透测试并将高危漏洞清零。
|
||||
|
||||
### 7.3 数据与埋点
|
||||
|
||||
统一指标字典,核心指标单一口径:播放(起播/卡顿/完播)、留存、收入、支付成功率。
|
||||
播放与收入类指标需与财务、商店账单对账,日报可追溯。
|
||||
|
||||
### 7.4 合规
|
||||
|
||||
隐私政策与用户协议随目标市场本地化;同意管理记录可审计;SDK 清单与商店 Data Safety / Privacy Labels 与实际数据流一致;
|
||||
账号注销与数据导出在承诺时限内完成。
|
||||
|
||||
## 8. 核心指标
|
||||
|
||||
- **北极星**:日均有效观看时长(有效定义:单次播放 ≥ 10s 且进度 ≥ 5%)。
|
||||
- **激活漏斗**:安装 → 首次启动 → 首页首播 → 完成 1 集 → 次日留存。
|
||||
- **商业化**:付费转化率、ARPPU、解锁集数、退款率、支付成功率。
|
||||
- **供给**:创作者上传量、审核通过率、平均审核时长、上架内容数。
|
||||
- **质量**:崩溃率、起播 P95、卡顿率、错误码分布。
|
||||
|
||||
## 9. 依赖与前置
|
||||
|
||||
以下前置项的关键路径在外部,研发无法加速,必须在 M0 并行启动(完整清单见评估表"外部前置与采购"):
|
||||
|
||||
1. 海外上架主体、DUNS / 企业验证、Apple 与 Google 组织账号;
|
||||
2. 公司域名、企业邮箱与官网(隐私政策与客服页依赖它);
|
||||
3. 隐私政策、用户协议、社区规范与版权政策;
|
||||
4. 海外短信通道(目标市场到达率与成本必须先压测);
|
||||
5. 云账号、对象存储、数据库、缓存、日志与 CDN(区域选择与预算同步确认);
|
||||
6. Apple / Google 支付账号、税务资料与商品配置;
|
||||
7. 内容版权台账(地区、期限、渠道、转授权范围必须结构化)。
|
||||
|
||||
## 10. 主要风险
|
||||
|
||||
| 风险 | 影响 | 应对 |
|
||||
| --- | --- | --- |
|
||||
| 市场/语言/内容形态未冻结 | 全链路返工 | 设为 M0 门禁,书面签署后进入变更控制 |
|
||||
| 商店账号与主体审核超期 | 无法提审 | 立项首周并行申请,准备备用主体 |
|
||||
| 支付一致性与跨账号权益 | 资损与投诉 | 幂等状态机 + 补偿任务 + 每日对账 + 客服入口 |
|
||||
| 视频与 CDN 成本失控 | 毛利被侵蚀 | 多码率策略、冷热分层、命中率与单位成本看板 |
|
||||
| 短信成本与到达率 | 无法登录 | 多供应商 + 频控 + 图形验证码 + 熔断 |
|
||||
| 内容安全与版权 | 下架或处罚 | 机审 + 人审 + 举报 + 申诉 + 结构化版权台账 |
|
||||
| 范围蔓延 | 无法形成可上线产品 | 冻结 MVP,变更需影响评估与批准 |
|
||||
|
||||
## 11. 里程碑
|
||||
|
||||
| 里程碑 | 内容 | 产出 |
|
||||
| --- | --- | --- |
|
||||
| M0 | 决策与前置 | 四项决策冻结、账号与主体、云与短信采购 |
|
||||
| M1 | 骨架与通道 | 双端与后端骨架、鉴权、存储转码链路、通道接入 |
|
||||
| M2 | 核心链路 | 账号、内容、播放器、记录收藏、创作者上传 |
|
||||
| M3 | 商业化 | 支付与权益、运营后台、内容治理 |
|
||||
| M4 | 提审准备 | 压测与安全测试、埋点验收、商店素材与提审 |
|
||||
| M5 | 上线与运营 | 灰度发布、值班与回滚演练、上线回归 |
|
||||
|
||||
## 12. 交付物清单
|
||||
|
||||
双端 App(iOS / Android)、创作者中心、运营后台、后端服务与 OpenAPI 契约、ERD、
|
||||
转码与媒资流水线、监控与埋点看板、测试与压测报告、安全测试报告、商店提审素材、发布与回滚 Runbook。
|
||||
Reference in new issue
Block a user