591 lines
20 KiB
JSON
591 lines
20 KiB
JSON
{
|
||
"openapi": "3.0.3",
|
||
"info": {
|
||
"title": "CTH TK App APIs",
|
||
"description": "Apifox import file for mini app drama and history APIs.",
|
||
"version": "1.0.0"
|
||
},
|
||
"servers": [
|
||
{
|
||
"url": "http://localhost:3000",
|
||
"description": "Local backend"
|
||
}
|
||
],
|
||
"tags": [
|
||
{
|
||
"name": "App Dramas",
|
||
"description": "Mini app drama APIs"
|
||
},
|
||
{
|
||
"name": "App History",
|
||
"description": "Mini app playback history APIs"
|
||
}
|
||
],
|
||
"paths": {
|
||
"/api/app/v1/dramas": {
|
||
"get": {
|
||
"tags": ["App Dramas"],
|
||
"summary": "短剧列表",
|
||
"operationId": "listAppDramas",
|
||
"parameters": [
|
||
{ "$ref": "#/components/parameters/Page" },
|
||
{ "$ref": "#/components/parameters/PageSize10" }
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "OK",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"data": {
|
||
"$ref": "#/components/schemas/AppDramaPage"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/api/app/v1/dramas/recommended": {
|
||
"get": {
|
||
"tags": ["App Dramas"],
|
||
"summary": "推荐短剧列表",
|
||
"operationId": "listRecommendedAppDramas",
|
||
"parameters": [
|
||
{ "$ref": "#/components/parameters/Page" },
|
||
{ "$ref": "#/components/parameters/PageSize10" }
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "OK",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"data": {
|
||
"$ref": "#/components/schemas/AppDramaPage"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/api/app/v1/dramas/presearch": {
|
||
"get": {
|
||
"tags": ["App Dramas"],
|
||
"summary": "关键字预查询视频列表",
|
||
"operationId": "presearchAppDramaVideos",
|
||
"parameters": [
|
||
{
|
||
"name": "keyword",
|
||
"in": "query",
|
||
"description": "用户输入关键字;空值返回空列表",
|
||
"required": false,
|
||
"schema": {
|
||
"type": "string"
|
||
},
|
||
"example": "skyline"
|
||
},
|
||
{ "$ref": "#/components/parameters/Page" },
|
||
{ "$ref": "#/components/parameters/PageSize10" }
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "OK",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"data": {
|
||
"$ref": "#/components/schemas/VideoPresearchPage"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"/api/app/v1/dramas/{id}/episodes": {
|
||
"get": {
|
||
"tags": ["App Dramas"],
|
||
"summary": "短剧剧集列表",
|
||
"operationId": "listAppDramaEpisodes",
|
||
"parameters": [
|
||
{ "$ref": "#/components/parameters/DramaIdPath" },
|
||
{ "$ref": "#/components/parameters/Page" },
|
||
{ "$ref": "#/components/parameters/PageSize20" }
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "OK",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"data": {
|
||
"$ref": "#/components/schemas/AppEpisodePage"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": { "$ref": "#/components/responses/ErrorResponse" }
|
||
}
|
||
}
|
||
},
|
||
"/api/app/v1/dramas/{id}": {
|
||
"get": {
|
||
"tags": ["App Dramas"],
|
||
"summary": "短剧详情",
|
||
"operationId": "getAppDrama",
|
||
"parameters": [
|
||
{ "$ref": "#/components/parameters/DramaIdPath" }
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "OK",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"data": {
|
||
"$ref": "#/components/schemas/AppDramaDetail"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"404": { "$ref": "#/components/responses/ErrorResponse" }
|
||
}
|
||
}
|
||
},
|
||
"/api/app/v1/history": {
|
||
"get": {
|
||
"tags": ["App History"],
|
||
"summary": "播放历史列表",
|
||
"operationId": "listAppHistory",
|
||
"security": [
|
||
{
|
||
"bearerAuth": []
|
||
}
|
||
],
|
||
"parameters": [
|
||
{ "$ref": "#/components/parameters/Page" },
|
||
{ "$ref": "#/components/parameters/PageSize20" }
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"description": "OK",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"data": {
|
||
"$ref": "#/components/schemas/PlaybackHistoryPage"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"401": { "$ref": "#/components/responses/ErrorResponse" }
|
||
}
|
||
},
|
||
"post": {
|
||
"tags": ["App History"],
|
||
"summary": "上报播放历史",
|
||
"operationId": "upsertAppHistory",
|
||
"security": [
|
||
{
|
||
"bearerAuth": []
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"required": true,
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/UpsertPlaybackHistoryRequest"
|
||
},
|
||
"example": {
|
||
"episodeId": 123,
|
||
"progressSeconds": 86,
|
||
"completed": false,
|
||
"occurredAt": "2026-07-02T10:00:00.000Z"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"201": {
|
||
"description": "Created",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"data": {
|
||
"$ref": "#/components/schemas/PlaybackHistoryRecord"
|
||
}
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"400": { "$ref": "#/components/responses/ErrorResponse" },
|
||
"401": { "$ref": "#/components/responses/ErrorResponse" },
|
||
"404": { "$ref": "#/components/responses/ErrorResponse" }
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"components": {
|
||
"securitySchemes": {
|
||
"bearerAuth": {
|
||
"type": "http",
|
||
"scheme": "bearer",
|
||
"bearerFormat": "JWT"
|
||
}
|
||
},
|
||
"parameters": {
|
||
"DramaIdPath": {
|
||
"name": "id",
|
||
"in": "path",
|
||
"required": true,
|
||
"schema": {
|
||
"type": "integer",
|
||
"minimum": 1
|
||
},
|
||
"example": 1
|
||
},
|
||
"Page": {
|
||
"name": "page",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": {
|
||
"type": "integer",
|
||
"minimum": 1,
|
||
"default": 1
|
||
}
|
||
},
|
||
"PageSize10": {
|
||
"name": "pageSize",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": {
|
||
"type": "integer",
|
||
"minimum": 1,
|
||
"maximum": 100,
|
||
"default": 10
|
||
}
|
||
},
|
||
"PageSize20": {
|
||
"name": "pageSize",
|
||
"in": "query",
|
||
"required": false,
|
||
"schema": {
|
||
"type": "integer",
|
||
"minimum": 1,
|
||
"maximum": 100,
|
||
"default": 20
|
||
}
|
||
}
|
||
},
|
||
"responses": {
|
||
"ErrorResponse": {
|
||
"description": "Error response",
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/ApiResponseBase" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"code": { "type": "integer", "example": 404 },
|
||
"data": { "type": "object", "example": {} },
|
||
"message": { "type": "string", "example": "Not found" }
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"schemas": {
|
||
"ApiResponseBase": {
|
||
"type": "object",
|
||
"required": ["code", "data", "message", "timestamp", "requestId", "cost"],
|
||
"properties": {
|
||
"code": {
|
||
"type": "integer",
|
||
"description": "业务响应码;成功为 0,错误为 HTTP 状态码",
|
||
"example": 0
|
||
},
|
||
"data": {
|
||
"type": "object"
|
||
},
|
||
"message": {
|
||
"type": "string",
|
||
"example": "success"
|
||
},
|
||
"timestamp": {
|
||
"type": "integer",
|
||
"format": "int64",
|
||
"example": 1782986400000
|
||
},
|
||
"requestId": {
|
||
"type": "string",
|
||
"example": "550e8400-e29b-41d4-a716-446655440000"
|
||
},
|
||
"cost": {
|
||
"type": "string",
|
||
"example": "12ms"
|
||
}
|
||
}
|
||
},
|
||
"Pagination": {
|
||
"type": "object",
|
||
"required": ["page", "pageSize", "total"],
|
||
"properties": {
|
||
"page": { "type": "integer", "example": 1 },
|
||
"pageSize": { "type": "integer", "example": 10 },
|
||
"total": { "type": "integer", "example": 100 }
|
||
}
|
||
},
|
||
"AppDrama": {
|
||
"type": "object",
|
||
"required": ["id", "title", "coverUrl", "type", "tags", "status", "sortOrder", "isRecommended", "onlineVersion", "totalEpisodes", "publishedEpisodes", "createdAt", "updatedAt"],
|
||
"properties": {
|
||
"id": { "type": "integer", "example": 1 },
|
||
"tiktokAlbumId": { "type": "string", "nullable": true },
|
||
"title": { "type": "string", "example": "Published Drama" },
|
||
"description": { "type": "string", "nullable": true },
|
||
"coverId": { "type": "integer", "nullable": true },
|
||
"coverUrl": { "type": "string", "example": "https://cdn.example.com/cover.jpg" },
|
||
"type": { "type": "string", "example": "romance" },
|
||
"tags": { "type": "array", "items": { "type": "string" } },
|
||
"region": { "type": "string", "nullable": true },
|
||
"language": { "type": "string", "nullable": true },
|
||
"year": { "type": "integer", "nullable": true },
|
||
"status": { "type": "string", "enum": ["draft", "scheduled", "published", "offline"], "example": "published" },
|
||
"sortOrder": { "type": "integer", "example": 0 },
|
||
"isRecommended": { "type": "boolean", "example": true },
|
||
"onlineVersion": { "type": "integer", "example": 1 },
|
||
"totalEpisodes": { "type": "integer", "example": 20 },
|
||
"publishedEpisodes": { "type": "integer", "example": 10 },
|
||
"createdAt": { "type": "string", "format": "date-time" },
|
||
"updatedAt": { "type": "string", "format": "date-time" }
|
||
}
|
||
},
|
||
"AppDramaDetail": {
|
||
"allOf": [
|
||
{ "$ref": "#/components/schemas/AppDrama" },
|
||
{
|
||
"type": "object",
|
||
"properties": {
|
||
"portraitCoverUrl": { "type": "string", "nullable": true },
|
||
"landscapeCoverUrl": { "type": "string", "nullable": true }
|
||
}
|
||
}
|
||
]
|
||
},
|
||
"AppDramaPage": {
|
||
"type": "object",
|
||
"required": ["list", "pagination"],
|
||
"properties": {
|
||
"list": {
|
||
"type": "array",
|
||
"items": { "$ref": "#/components/schemas/AppDrama" }
|
||
},
|
||
"pagination": { "$ref": "#/components/schemas/Pagination" }
|
||
}
|
||
},
|
||
"AppEpisode": {
|
||
"type": "object",
|
||
"required": ["id", "dramaId", "albumId", "episodeNo", "seq", "title", "coverUrl", "durationSeconds", "isPaid", "freeType", "price", "status", "reviewStatus", "publishStatus", "createdAt", "updatedAt"],
|
||
"properties": {
|
||
"id": { "type": "integer", "example": 1 },
|
||
"dramaId": { "type": "integer", "example": 1 },
|
||
"albumId": { "type": "integer", "example": 1 },
|
||
"tiktokEpisodeId": { "type": "string", "nullable": true },
|
||
"episodeNo": { "type": "integer", "example": 1 },
|
||
"seq": { "type": "integer", "example": 1 },
|
||
"title": { "type": "string", "example": "Episode 1" },
|
||
"description": { "type": "string", "nullable": true },
|
||
"byteplusVid": { "type": "string", "nullable": true },
|
||
"coverId": { "type": "integer", "nullable": true },
|
||
"coverUrl": { "type": "string", "example": "https://cdn.example.com/episode.jpg" },
|
||
"videoUrl": { "type": "string", "nullable": true },
|
||
"storageBucket": { "type": "string", "nullable": true },
|
||
"objectKey": { "type": "string", "nullable": true },
|
||
"trialDurationSeconds": { "type": "integer", "nullable": true },
|
||
"durationSeconds": { "type": "integer", "example": 300 },
|
||
"width": { "type": "integer", "nullable": true },
|
||
"height": { "type": "integer", "nullable": true },
|
||
"isPaid": { "type": "boolean", "example": false },
|
||
"freeType": { "type": "string", "enum": ["free", "paid"], "example": "free" },
|
||
"unlockPrice": { "type": "integer", "nullable": true },
|
||
"price": { "type": "integer", "example": 0 },
|
||
"publishAt": { "type": "string", "format": "date-time", "nullable": true },
|
||
"nextReleaseAt": { "type": "string", "format": "date-time", "nullable": true },
|
||
"status": { "type": "string", "enum": ["draft", "scheduled", "published", "offline"], "example": "published" },
|
||
"reviewStatus": { "type": "string", "enum": ["not_submitted", "pending", "reviewing", "approved", "rejected"], "example": "approved" },
|
||
"reviewMessage": { "type": "string", "nullable": true },
|
||
"publishStatus": { "type": "string", "enum": ["draft", "scheduled", "published", "offline"], "example": "published" },
|
||
"createdAt": { "type": "string", "format": "date-time" },
|
||
"updatedAt": { "type": "string", "format": "date-time" }
|
||
}
|
||
},
|
||
"AppEpisodePage": {
|
||
"type": "object",
|
||
"required": ["list", "pagination"],
|
||
"properties": {
|
||
"list": {
|
||
"type": "array",
|
||
"items": { "$ref": "#/components/schemas/AppEpisode" }
|
||
},
|
||
"pagination": { "$ref": "#/components/schemas/Pagination" }
|
||
}
|
||
},
|
||
"VideoPresearchItem": {
|
||
"type": "object",
|
||
"required": ["id", "dramaId", "episodeNo", "title", "coverUrl", "durationSeconds", "freeType", "price", "dramaTitle", "dramaCoverUrl"],
|
||
"properties": {
|
||
"id": { "type": "integer", "example": 1 },
|
||
"dramaId": { "type": "integer", "example": 1 },
|
||
"episodeNo": { "type": "integer", "example": 1 },
|
||
"title": { "type": "string", "example": "Pilot Video Match" },
|
||
"description": { "type": "string", "nullable": true },
|
||
"coverUrl": { "type": "string", "example": "https://cdn.example.com/episode.jpg" },
|
||
"durationSeconds": { "type": "integer", "example": 180 },
|
||
"byteplusVid": { "type": "string", "nullable": true },
|
||
"videoUrl": { "type": "string", "nullable": true },
|
||
"freeType": { "type": "string", "enum": ["free", "paid"], "example": "free" },
|
||
"price": { "type": "integer", "example": 0 },
|
||
"dramaTitle": { "type": "string", "example": "Skyline Keyword Saga" },
|
||
"dramaCoverUrl": { "type": "string", "example": "https://cdn.example.com/drama.jpg" }
|
||
}
|
||
},
|
||
"VideoPresearchPage": {
|
||
"type": "object",
|
||
"required": ["list", "pagination"],
|
||
"properties": {
|
||
"list": {
|
||
"type": "array",
|
||
"items": { "$ref": "#/components/schemas/VideoPresearchItem" }
|
||
},
|
||
"pagination": { "$ref": "#/components/schemas/Pagination" }
|
||
}
|
||
},
|
||
"UpsertPlaybackHistoryRequest": {
|
||
"type": "object",
|
||
"required": ["episodeId", "progressSeconds"],
|
||
"properties": {
|
||
"episodeId": { "type": "integer", "minimum": 1, "example": 123 },
|
||
"progressSeconds": { "type": "integer", "minimum": 0, "example": 86 },
|
||
"completed": { "type": "boolean", "example": false },
|
||
"occurredAt": { "type": "string", "format": "date-time", "example": "2026-07-02T10:00:00.000Z" }
|
||
}
|
||
},
|
||
"PlaybackHistoryRecord": {
|
||
"type": "object",
|
||
"required": ["id", "userId", "dramaId", "episodeId", "progressSeconds", "durationSeconds", "completed", "lastPlayedAt", "createdAt", "updatedAt"],
|
||
"properties": {
|
||
"id": { "type": "integer", "example": 1 },
|
||
"userId": { "type": "integer", "example": 1 },
|
||
"dramaId": { "type": "integer", "example": 1 },
|
||
"episodeId": { "type": "integer", "example": 123 },
|
||
"progressSeconds": { "type": "integer", "example": 86 },
|
||
"durationSeconds": { "type": "integer", "example": 300 },
|
||
"completed": { "type": "boolean", "example": false },
|
||
"lastPlayedAt": { "type": "string", "format": "date-time" },
|
||
"createdAt": { "type": "string", "format": "date-time" },
|
||
"updatedAt": { "type": "string", "format": "date-time" }
|
||
}
|
||
},
|
||
"PlaybackHistoryListItem": {
|
||
"type": "object",
|
||
"required": ["id", "dramaId", "episodeId", "episodeNo", "title", "coverUrl", "progressSeconds", "durationSeconds", "completed", "lastPlayedAt", "dramaTitle", "dramaCoverUrl"],
|
||
"properties": {
|
||
"id": { "type": "integer", "example": 1 },
|
||
"dramaId": { "type": "integer", "example": 1 },
|
||
"episodeId": { "type": "integer", "example": 123 },
|
||
"episodeNo": { "type": "integer", "example": 1 },
|
||
"title": { "type": "string", "example": "History Episode" },
|
||
"coverUrl": { "type": "string", "example": "https://cdn.example.com/history-episode.jpg" },
|
||
"progressSeconds": { "type": "integer", "example": 86 },
|
||
"durationSeconds": { "type": "integer", "example": 300 },
|
||
"completed": { "type": "boolean", "example": false },
|
||
"lastPlayedAt": { "type": "string", "format": "date-time" },
|
||
"dramaTitle": { "type": "string", "example": "History Drama" },
|
||
"dramaCoverUrl": { "type": "string", "example": "https://cdn.example.com/history-drama.jpg" }
|
||
}
|
||
},
|
||
"PlaybackHistoryPage": {
|
||
"type": "object",
|
||
"required": ["list", "pagination"],
|
||
"properties": {
|
||
"list": {
|
||
"type": "array",
|
||
"items": { "$ref": "#/components/schemas/PlaybackHistoryListItem" }
|
||
},
|
||
"pagination": { "$ref": "#/components/schemas/Pagination" }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|