网盘系统 API 接口文档

微服务架构 · 统一网关入口

网关地址:http://localhost:8888

服务架构图

前端
Gateway:8888
User:8081
Drive:8082
TMDB:8083
Drive:8082
OpenList:13000
各网盘

路由规则

请求路径转发服务说明
/api/login, /api/registeruser-service登录注册
/api/token/**, /api/login-logs/**user-service令牌/日志
/api/users/**user-service用户管理
/api/profile/**user-service个人资料
/api/drive/**drive-service网盘管理
/api/drive/**/sync-filesdrive-serviceOpenList 文件同步
/api/drive/**/play-urldrive-serviceOpenList 播放链接
/api/icon/**drive-serviceIcon管理
/api/movies/**tmdb-service电影管理
/api/tmdb/**, /api/tmdb-scraper/**tmdb-serviceTMDB刮削

用户认证服务

wangpan-user-service : 8081
POST /api/login 用户登录

请求体

{ "username": "用户名", "password": "密码" }

成功响应示例

{ "code": 200, "message": "登录成功", "data": { "id": 7, "username": "zhangsan", "userType": 0, // 0=普通用户, 1=VIP, 2=管理员 "status": 1, "email": "xxx@xx.com", "phone": "138****0000", "realName": "张三", "avatarUrl": "", "lastLoginIp": "127.0.0.1", "lastLoginTime": "2026-06-10T11:20:21", "hasTmdbKey": false, "token": "tk_xxx...", "tokenType": "Bearer", "expireIn": 604800 } }

失败响应示例 — 用户名或密码错误

{ "code": 400, "message": "用户名或密码错误", "data": null }

失败响应示例 — 账号被禁用

{ "code": 403, "message": "账号已被禁用,请联系管理员", "data": null }

失败响应示例 — 参数缺失

{ "code": 400, "message": "用户名和密码不能为空", "data": null }

userType 说明

角色权限说明
0普通用户仅访问自己的数据(如日志只看自己的)
1VIP 用户普通用户 + 更多存储空间
2管理员可查看所有用户数据(如全部日志)
POST /api/register 用户注册

请求体

{ "username": "用户名", "password": "密码" }

成功响应示例

{ "code": 200, "message": "注册成功", "data": { "id": 10, "username": "newuser" } }

失败响应示例 — 用户名已存在

{ "code": 409, "message": "用户名已被占用", "data": null }

失败响应示例 — 参数缺失

{ "code": 400, "message": "用户名和密码不能为空", "data": null }
GET /api/token/validate 令牌验证

请求头

Authorization: Bearer <token>

成功响应示例 — Token 有效

{ "code": 200, "message": "Token 有效", "data": { "valid": true, "userId": 7, "username": "zhangsan", "expireTime": "2026-06-17T11:20:21" } }

失败响应示例 — Token 无效或已过期

{ "code": 401, "message": "Token 无效或已过期", "data": null }
GET /api/login-logs 登录日志查询需Token

权限说明

用户类型可见范围
普通用户(userType=0/1)仅自己的登录记录
管理员(userType=2)所有用户的登录记录

查询参数

参数类型必填说明
usernameString按用户名模糊筛选(管理员可用)
resultString登录结果:success / fail
startDateString开始日期(含),格式:yyyy-MM-dd,如 2026-06-01
endDateString结束日期(含),格式:yyyy-MM-dd,如 2026-06-10
pageNoint页码,默认 1
pageSizeint每页条数,默认 20

提示:可组合使用多个筛选条件,如 ?result=fail&startDate=2026-06-01&endDate=2026-06-10

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "total": 10, "list": [ { "id": 1, "userId": 7, "username": "zhangsan", "loginIp": "127.0.0.1", "loginTime": "2026-06-10T11:20:21", "result": "success", "userAgent": "Mozilla/5.0..." } ], "pageNo": 1, "pageSize": 20 } }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "权限不足,无法查看其他用户的登录日志", "data": null }

用户管理

wangpan-user-service : 8081 仅管理员 (userType=2)
GET /api/users 查询用户列表需Token + 管理员

权限说明

角色访问权限
普通/VIP 用户403 权限不足
管理员可查看所有用户列表

查询参数

参数类型必填说明
usernameString用户名模糊搜索
statusint状态:0=禁用, 1=正常, 2=锁定
userTypeint角色:0=普通, 1=VIP, 2=管理员
pageNoint页码,默认 1
pageSizeint每页条数,默认 20

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "total": 5, "list": [ { "id": 7, "username": "zhangsan", "userType": 0, "status": 1, ... } ], "pageNo": 1, "pageSize": 20 } }

失败响应示例 — 权限不足

{ "code": 403, "message": "权限不足,仅管理员可访问", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
POST /api/users 新增用户需Token + 管理员

请求体

{ "username": "必填", "password": "必填", "email": "可选", "phone": "可选", "realName": "可选", "status": 1, // 可选:0=禁用, 1=正常, 默认1 "userType": 0 // 可选:0=普通, 1=VIP, 2=管理员, 默认0 }

成功响应

{ "code": 200, "message": "用户创建成功", "data": { "id": 10, "username": "newuser", "email": "new@test.com", "status": 1, "userType": 0 } }

失败响应示例 — 用户名已存在

{ "code": 409, "message": "用户名已被占用", "data": null }

失败响应示例 — 参数缺失

{ "code": 400, "message": "用户名或密码不能为空", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "非管理员,无权限操作", "data": null }

失败响应示例 — 服务器错误

{ "code": 500, "message": "服务器内部错误", "data": null }
PUT /api/users/{userId} 修改用户信息需Token + 管理员

路径参数

参数类型说明
userIdLong目标用户 ID

请求体(部分更新,只传需要修改的字段)

{ "email": "新邮箱", // 可选 "phone": "新手机号", // 可选 "realName": "新姓名", // 可选 "status": 0, // 可选:0=禁用, 1=正常, 2=锁定 "userType": 1 // 可选:0=普通, 1=VIP, 2=管理员 }

成功响应

{ "code": 200, "message": "用户信息更新成功", "data": { "id": 10, "username": "newuser", "email": "new@test.com", "status": 1, "userType": 0 } }

失败响应示例 — 用户不存在

{ "code": 404, "message": "目标用户不存在", "data": null }

失败响应示例 — 参数缺失

{ "code": 400, "message": "没有需要更新的字段", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "非管理员,无权限操作", "data": null }

失败响应示例 — 服务器错误

{ "code": 500, "message": "服务器内部错误", "data": null }
DELETE /api/users/{userId} 删除用户(软删除)需Token + 管理员

路径参数

参数类型说明
userIdLong要删除的用户 ID

说明

软删除操作,将 is_deleted 设为 1,不物理删除数据。

成功响应

{ "code": 200, "message": "用户删除成功", "data": null }

失败响应示例 — 用户不存在

{ "code": 404, "message": "目标用户不存在", "data": null }

失败响应示例 — 删除失败

{ "code": 400, "message": "删除失败,用户可能已被删除", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "非管理员,无权限操作", "data": null }

失败响应示例 — 服务器错误

{ "code": 500, "message": "服务器内部错误", "data": null }

个人资料

wangpan-user-service : 8081
GET /api/profile 获取当前用户个人资料需Token

功能说明

获取当前登录用户的基本信息(不含密码等敏感字段)。

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "id": 1, "username": "admin", "email": "admin@example.com", "phone": "13800138000", "realName": "管理员", "avatarUrl": "http://...", "userType": 2, "status": 1, "createTime": "2024-01-01T00:00:00" } }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }

失败响应示例 — 用户不存在

{ "code": 404, "message": "用户不存在", "data": null }
PUT /api/profile/username 修改用户名需Token

功能说明

修改当前登录用户的用户名。

校验规则:

  • 用户名不能为空
  • 新用户名不能与当前用户名相同
  • 新用户名不能被其他用户占用

请求体

{ "username": "新用户名" }

成功响应示例

{ "code": 200, "message": "用户名修改成功", "data": null }

失败响应示例 — 用户名已存在

{ "code": 409, "message": "用户名已被其他用户使用", "data": null }

失败响应示例 — 参数错误

{ "code": 400, "message": "新用户名不能为空或与当前用户名相同", "data": null }
PUT /api/profile/avatar 修改头像需Token

功能说明

修改当前登录用户的头像。

流程:

  1. 接收 Base64 编码的图片数据
  2. 解析 MIME 类型和 Base64 内容
  3. 上传到 MinIO(路径:avatar/{userId}_{timestamp}.{ext})
  4. 更新数据库中的 avatar_url 字段

支持的图片格式:PNG、JPG/JPEG、GIF、WebP

请求体

{ "avatarData": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." }

成功响应示例

{ "code": 200, "message": "头像更新成功", "data": { "avatarUrl": "http://your-server:39000/wangpan/avatar/1_1719234567890.png?..." } }

失败响应示例 — 图片格式不支持

{ "code": 400, "message": "不支持的图片格式,仅支持 PNG、JPG、GIF、WebP", "data": null }

失败响应示例 — MinIO 上传失败

{ "code": 500, "message": "头像上传失败,请稍后重试", "data": null }

网盘管理服务

wangpan-drive-service : 8082
POST /api/drive 创建网盘需Token

请求体

{ "name": "网盘名称", "description": "网盘描述", "logoData": "data:image/png;base64,iVBOR..." }

参数说明

参数类型必填说明
nameString网盘名称
descriptionString网盘描述
logoDataStringLogo图片Base64编码,支持 data:image/xxx;base64, 前缀格式,上传后存储到MinIO,数据库仅保存URL

成功响应示例

{ "code": 200, "message": "创建成功", "data": { "id": 1, "name": "我的网盘", "description": "个人网盘", "logoUrl": "http://your-server:39000/wangpan/drive/1/logo.png", "createTime": "2026-06-26T10:00:00" } }

失败响应示例 — 参数缺失

{ "code": 400, "message": "网盘名称不能为空", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
GET /api/drive 查询网盘列表需Token

查询参数

参数默认值说明
pageNo1页码
pageSize20每页条数

响应字段

字段类型说明
idLong网盘ID
nameString网盘名称
descriptionString网盘描述
logoUrlString网盘Logo图片链接(MinIO存储地址)
folderCountInteger文件夹数量
fileCountInteger文件数量
totalSizeLong总大小(字节)
statusString状态
createTimeString创建时间
updateTimeString更新时间

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "total": 2, "list": [ { "id": 1, "name": "我的网盘", "description": "个人网盘", "logoUrl": "http://your-server:39000/wangpan/drive/1/logo.png", "folderCount": 5, "fileCount": 20, "totalSize": 10737418240, "status": "active", "createTime": "2026-06-26T10:00:00", "updateTime": "2026-06-26T10:00:00" } ], "pageNo": 1, "pageSize": 20 } }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
GET /api/drive/{id} 查询网盘详情需Token

路径参数

参数类型说明
idLong网盘ID

响应字段

字段类型说明
idLong网盘ID
nameString网盘名称
descriptionString网盘描述
logoUrlString网盘Logo图片链接(MinIO存储地址)
folderCountInteger文件夹数量
fileCountInteger文件数量
totalSizeLong总大小(字节)
statusString状态
createTimeString创建时间
updateTimeString更新时间

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "id": 1, "name": "我的网盘", "description": "个人网盘", "logoUrl": "http://your-server:39000/wangpan/drive/1/logo.png", "folderCount": 5, "fileCount": 20, "totalSize": 10737418240, "status": "active", "createTime": "2026-06-26T10:00:00", "updateTime": "2026-06-26T10:00:00" } }

失败响应示例 — 网盘不存在

{ "code": 404, "message": "网盘不存在", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
PUT /api/drive/{id} 更新网盘需Token

请求体

{ "name": "新网盘名称", "description": "新描述", "logoData": "data:image/png;base64,iVBOR..." }

参数说明

参数类型必填说明
nameString网盘名称
descriptionString网盘描述
logoDataString新的Logo图片Base64编码,传入后覆盖旧Logo;不传则保留原Logo

成功响应示例

{ "code": 200, "message": "更新成功", "data": { "id": 1, "name": "新网盘名称", "description": "新描述", "logoUrl": "http://your-server:39000/wangpan/drive/1/logo.png", "updateTime": "2026-06-26T11:00:00" } }

失败响应示例 — 网盘不存在

{ "code": 404, "message": "网盘不存在", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "无权修改该网盘", "data": null }
DELETE /api/drive/{id} 删除网盘需Token

路径参数

参数类型说明
idLong网盘ID

成功响应示例

{ "code": 200, "message": "删除成功", "data": null }

失败响应示例 — 网盘不存在

{ "code": 404, "message": "网盘不存在", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "无权删除该网盘", "data": null }
POST /api/drive/{driveId}/folders 创建文件夹需Token

请求体

{ "name": "文件夹名称", "parentId": 0 }

成功响应示例

{ "code": 200, "message": "创建成功", "data": { "id": 1, "driveId": 1, "parentId": 0, "name": "我的电影", "description": "", "depth": 0, "createTime": "2026-06-26T10:00:00" } }

失败响应示例 — 参数缺失

{ "code": 400, "message": "文件夹名称不能为空", "data": null }

失败响应示例 — 网盘不存在

{ "code": 404, "message": "网盘不存在", "data": null }
GET /api/drive/{driveId}/folders 查询文件夹列表需Token

查询参数

参数默认值说明
parentId0父文件夹 ID

成功响应示例

{ "code": 200, "message": "查询成功", "data": [ { "id": 1, "driveId": 1, "parentId": 0, "name": "我的电影", "description": "", "depth": 0, "childFolderCount": 2, "fileCount": 5, "createTime": "2026-06-26T10:00:00" } ] }

失败响应示例 — 网盘不存在

{ "code": 404, "message": "网盘不存在", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
GET /api/drive/{driveId}/folders/{folderId} 查询文件夹详情需Token

路径参数

参数类型说明
driveIdLong网盘ID
folderIdLong文件夹ID

响应字段

字段类型说明
idLong文件夹ID
driveIdLong所属网盘ID
parentIdLong父文件夹ID(0表示根目录)
nameString文件夹名称
descriptionString文件夹描述
depthInteger层级深度
childFolderCountInteger子文件夹数量
fileCountInteger文件数量
totalSizeLong总大小(字节)
createTimeString创建时间
updateTimeString更新时间

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "id": 1, "driveId": 1, "parentId": 0, "name": "我的电影", "description": "收藏的电影", "depth": 0, "childFolderCount": 2, "fileCount": 5, "totalSize": 5368709120, "createTime": "2026-06-26T10:00:00", "updateTime": "2026-06-26T10:00:00" } }

失败响应示例 — 文件夹不存在

{ "code": 404, "message": "文件夹不存在", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
PUT /api/drive/{driveId}/folders/{folderId} 更新文件夹需Token

路径参数

参数类型说明
driveIdLong网盘ID
folderIdLong文件夹ID

请求体(部分更新)

{ "name": "新文件夹名称", "description": "新描述", "parentId": 0 }

参数说明

参数类型必填说明
nameString文件夹名称
descriptionString文件夹描述
parentIdLong父文件夹ID(可移动文件夹)

成功响应示例

{ "code": 200, "message": "更新成功", "data": { "id": 1, "driveId": 1, "parentId": 0, "name": "新文件夹名称", "description": "新描述", "updateTime": "2026-06-26T11:00:00" } }

失败响应示例 — 文件夹不存在

{ "code": 404, "message": "文件夹不存在", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "无权修改该文件夹", "data": null }
DELETE /api/drive/{driveId}/folders/{folderId} 删除文件夹需Token

路径参数

参数类型说明
driveIdLong网盘ID
folderIdLong文件夹ID

说明

删除文件夹前会检查文件夹是否为空,如果包含子文件夹或电影文件则拒绝删除。

错误码

code说明
400文件夹不为空或数据库操作失败
401未登录或Token无效
403无权访问该文件夹
POST /api/drive/{driveId}/movies 创建电影(只传文件名)需Token

路径参数

参数类型说明
driveIdLong网盘ID

请求体

{ "title": "文件名.mp4", // 必填,文件名(同时作为电影标题) "folderId": 0 // 可选,默认0(根目录) }

参数说明

参数类型必填说明
titleString文件名(同时作为电影标题,后续通过 TMDB 刮削补充信息)
folderIdLong目标文件夹ID,默认0(根目录)

说明

只记录文件名,不上传文件数据。创建后通过 TMDB 刮削接口补充电影的海报、描述、评分等信息。自动检测 MIME 类型(mp4/mkv/avi → video/mp4)。

成功响应示例

{ "code": 200, "message": "创建成功", "data": { "id": 1, "driveId": 1, "folderId": 0, "title": "电影.mp4", "originalName": "电影.mp4", "mimeType": "video/mp4", "createTime": "2026-06-26T10:00:00" } }

失败响应示例 — 参数缺失

{ "code": 400, "message": "文件名不能为空", "data": null }

失败响应示例 — 网盘不存在

{ "code": 404, "message": "网盘不存在", "data": null }
GET /api/drive/{driveId}/folders/{folderId}/movies 查询电影文件列表需Token

路径参数

参数类型说明
driveIdLong网盘ID
folderIdLong文件夹ID(0表示根目录)

查询参数

参数默认值说明
pageNo1页码
pageSize20每页条数(最大100)

响应字段

字段类型说明
list[].idLong电影ID
list[].titleString电影名称
list[].originalNameString原始文件名
list[].mimeTypeStringMIME类型
list[].fileSizeLong文件大小(字节)
list[].extensionString文件扩展名
list[].minioUrlStringMinIO文件访问URL
list[].ratingInteger评分
list[].tagsString标签
totalInteger总数
pageNoInteger当前页码
pageSizeInteger每页条数

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "total": 5, "pageNo": 1, "pageSize": 20, "list": [ { "id": 1, "title": "电影.mp4", "originalName": "电影.mp4", "mimeType": "video/mp4", "fileSize": 1073741824, "extension": "mp4", "minioUrl": "http://...", "rating": 85, "tags": "动作,科幻" } ] } }

失败响应示例 — 网盘不存在

{ "code": 404, "message": "网盘不存在", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
GET /api/drive/{driveId}/movies/{movieId} 查询电影文件详情需Token

路径参数

参数类型说明
driveIdLong网盘ID
movieIdLong电影ID

响应字段

字段类型说明
idLong电影ID
driveIdLong所属网盘ID
folderIdLong所属文件夹ID
titleString电影名称
mimeTypeStringMIME类型
fileSizeLong文件大小(字节)
extensionString文件扩展名
descriptionString描述
maintainerString维护者
statusString状态
scrapeTypeString刮削类型
scrapeStatusString刮削状态
ratingInteger|null评分(刮削成功时返回)
tagsString标签
castsString|null主演(刮削成功时返回)
directorsString|null导演(刮削成功时返回)
posterUrlString|null海报URL(刮削成功时返回)
backdropUrlString|null背景图URL(刮削成功时返回)
createTimeString创建时间
updateTimeString更新时间

成功响应示例(刮削成功)

{ "code": 200, "message": "查询成功", "data": { "id": 1, "driveId": 1, "folderId": 0, "title": "阿凡达.mp4", "mimeType": "video/mp4", "fileSize": 1073741824, "extension": "mp4", "description": "电影描述", "maintainer": "admin", "status": "active", "scrapeType": "tmdb", "scrapeStatus": "success", "rating": 85, "tags": "动作,科幻", "casts": "主演1,主演2", "directors": "导演1", "posterUrl": "http://...", "backdropUrl": "http://...", "createTime": "2026-06-26T10:00:00", "updateTime": "2026-06-26T10:00:00" } }

成功响应示例(未刮削/刮削失败)

{ "code": 200, "message": "查询成功", "data": { "id": 2, "driveId": 1, "folderId": 0, "title": "未刮削电影.mp4", "mimeType": "video/mp4", "fileSize": 536870912, "extension": "mp4", "description": null, "maintainer": null, "status": "active", "scrapeType": "none", "scrapeStatus": "pending", "rating": null, "tags": null, "casts": null, "directors": null, "posterUrl": null, "backdropUrl": null, "createTime": "2026-06-26T10:00:00", "updateTime": "2026-06-26T10:00:00" } }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
PUT /api/drive/{driveId}/movies/{movieId} 更新电影文件信息需Token

路径参数

参数类型说明
driveIdLong网盘ID
movieIdLong电影ID

请求体(部分更新,所有字段可选)

{ "title": "新电影名称", "description": "新描述", "rating": 90, "tags": "新标签", "posterUrl": "海报URL", "backdropUrl": "背景图URL", "casts": "主演列表", "directors": "导演列表", "resourceLibrary": "资源库来源", "maintainer": "维护者" }

参数说明

参数类型说明
titleString电影名称
descriptionString描述
ratingInteger评分 0-100
tagsString标签
posterUrlString海报URL
backdropUrlString背景图URL
castsString主演(逗号分隔)
directorsString导演(逗号分隔)
resourceLibraryString资源库来源
maintainerString维护者

说明

用于 TMDB 刮削有误时,用户可自定义修改文件名等信息。所有字段均为可选,只更新传入的字段。

成功响应示例

{ "code": 200, "message": "更新成功", "data": { "id": 1, "title": "新电影名称", "rating": 90, "updateTime": "2026-06-26T11:00:00" } }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "无权修改该电影", "data": null }
DELETE /api/drive/{driveId}/movies/{movieId} 删除电影文件需Token

路径参数

参数类型说明
driveIdLong网盘ID
movieIdLong电影ID

说明

逻辑删除(软删除),删除后自动更新网盘和文件夹的统计信息。

成功响应示例

{ "code": 200, "message": "删除成功", "data": null }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在", "data": null }

失败响应示例 — 权限不足

{ "code": 403, "message": "无权删除该电影", "data": null }
GET /api/drive/movies/duplicates 检查重复电影需Token

功能说明

查询当前用户所有网盘下所有文件夹中的电影,按标题(title)分组找出重复的电影。返回每组重复电影的详细信息,包括所在文件夹的完整路径,前端可让用户选择删除其中一个。

成功响应

{ "code": 200, "message": "查询成功", "data": { "duplicateGroups": [ { "title": "电影名称", "count": 2, "movies": [ { "id": 10, "driveId": 1, "folderId": 5, "title": "电影名称", "originalName": "电影名称.mp4", "fileSize": 1073741824, "extension": "mp4", "category": "sci-fi", "rating": 85, "createTime": "2025-01-01T10:00:00", "folderPath": "我的电影库/科幻/2024" }, { "id": 20, "driveId": 1, "folderId": 8, "title": "电影名称", "originalName": "电影名称.mkv", "fileSize": 2147483648, "extension": "mkv", "category": "sci-fi", "rating": null, "createTime": "2025-02-01T15:00:00", "folderPath": "我的电影库/动作" } ] } ], "totalGroups": 1, "totalDuplicates": 2 } }

返回字段说明

字段类型说明
duplicateGroupsArray重复电影分组列表
duplicateGroups[].titleString重复的电影标题
duplicateGroups[].countInteger该标题下的重复数量
duplicateGroups[].moviesArray该标题下的所有电影记录
duplicateGroups[].movies[].idLong电影ID(可用于删除接口)
duplicateGroups[].movies[].folderPathString所在文件夹完整路径,如"网盘名/文件夹1/文件夹2"
totalGroupsInteger重复组总数
totalDuplicatesInteger涉及重复的电影总数

错误码

code说明
401未登录或 Token 无效
500服务器内部错误

Icon 管理服务

wangpan-drive-service : 8082
POST /api/icon 创建 Icon需Token

请求体(JSON)

字段类型必填说明
nameStringIcon 名称(最大100字符)
urlString条件必填外网链接 URL(最大500字符,linkType为external或both时必填)
internalUrlString条件必填内网链接 URL(最大500字符,linkType为internal或both时必填)
linkTypeString链接类型:external(仅外网,默认)/ internal(仅内网)/ both(内外网)
descriptionStringIcon 描述(最大500字符)
sortOrderInteger排序值,数字越小越靠前,默认0

请求示例(仅外网)

{ "name": "百度", "url": "https://www.example.com", "linkType": "external", "description": "搜索引擎", "sortOrder": 1 }

请求示例(内外网都有)

{ "name": "内部系统", "url": "https://system.example.com", "internalUrl": "http://192.168.x.x:8080", "linkType": "both", "description": "内部管理系统", "sortOrder": 2 }

成功响应(200)

{ "code": 200, "message": "创建成功", "data": { "id": 1, "name": "百度", "url": "https://www.example.com", "internalUrl": null, "linkType": "external", "description": "搜索引擎", "sortOrder": 1, "status": "active", "logoUrl": "http://your-server:39000/wangpan/icons/2/1718265600000.png", "createTime": "2026-06-13T16:00:00" } }
GET /api/icon 查询当前用户的 Icon 列表需Token

功能说明

查询当前登录用户的所有 Icon,按 sortOrder 升序、createTime 降序排列。每个用户只能看到自己创建的 Icon。

成功响应(200)

{ "code": 200, "message": "查询成功", "data": { "list": [ { "id": 1, "name": "百度", "url": "https://www.example.com", "internalUrl": null, "linkType": "external", "description": "搜索引擎", "sortOrder": 1, "status": "active", "logoUrl": "http://your-server:39000/wangpan/icons/2/1718265600000.png", "createTime": "2026-06-13T16:00:00", "updateTime": "2026-06-13T16:00:00" } ], "total": 1 } }
GET /api/icon/{id} 查询单个 Icon 详情需Token

路径参数

参数类型说明
idLongIcon ID

成功响应(200)

{ "code": 200, "message": "查询成功", "data": { "id": 1, "name": "百度", "url": "https://www.example.com", "internalUrl": null, "linkType": "external", "description": "搜索引擎", "sortOrder": 1, "status": "active", "logoUrl": "http://your-server:39000/wangpan/icons/2/1718265600000.png", "createTime": "2026-06-13T16:00:00", "updateTime": "2026-06-13T16:00:00" } }

失败响应示例 — Icon 不存在

{ "code": 404, "message": "Icon 不存在或无权访问", "data": null }

失败响应示例 — 未登录

{ "code": 401, "message": "未登录或 Token 无效", "data": null }
PUT /api/icon/{id} 更新 Icon需Token

路径参数

参数类型说明
idLongIcon ID

请求体(JSON,所有字段可选)

字段类型说明
nameStringIcon 名称
urlString外网链接
internalUrlString内网链接
linkTypeString链接类型:external / internal / both
descriptionStringIcon 描述
sortOrderInteger排序值

请求示例

{ "name": "新名称", "url": "https://new-url.com", "internalUrl": "http://192.168.x.x:8080", "linkType": "both", "sortOrder": 2 }

成功响应(200)

{ "code": 200, "message": "更新成功", "data": { "id": 1, "name": "新名称", "url": "https://new-url.com", "internalUrl": "http://192.168.x.x:8080", "linkType": "both", "description": "搜索引擎", "sortOrder": 2, "status": "active", "logoUrl": "http://your-server:39000/wangpan/icons/2/1718265600000.png", "createTime": "2026-06-13T16:00:00", "updateTime": "2026-06-13T17:00:00" } }

错误码

code说明
401未登录或 Token 无效
500Icon 不存在或无权修改
DELETE /api/icon/{id} 删除 Icon需Token

路径参数

参数类型说明
idLongIcon ID

成功响应(200)

{ "code": 200, "message": "删除成功", "data": null }

错误码

code说明
401未登录或 Token 无效
500Icon 不存在或无权删除
POST /api/icon/{id}/logo 上传自定义图标图片需Token

功能说明

上传自定义图标图片替换自动抓取的 logo。上传后会自动删除旧的 MinIO 图片,防止硬盘空间浪费。系统每 24 小时自动清理无用图片。

路径参数

参数类型说明
idLongIcon ID

请求格式

multipart/form-data

字段类型必填说明
fileFile图片文件(png/jpg/jpeg/gif/webp/ico,最大 1MB)

请求示例(curl)

curl -X POST http://localhost:8888/api/icon/1/logo \ -H "Authorization: Bearer <token>" \ -F "file=@/path/to/icon.png"

成功响应(200)

{ "code": 200, "message": "图标上传成功", "data": { "id": 1, "name": "百度", "logoUrl": "http://your-server:39000/wangpan/icons/2/1718265600000.png", "updateTime": "2026-06-15T10:00:00" } }

失败响应示例 — 文件为空

{ "code": 400, "message": "上传文件为空", "data": null }

失败响应示例 — 文件格式不支持

{ "code": 400, "message": "不支持的文件格式,仅支持 png/jpg/jpeg/gif/webp/ico", "data": null }

失败响应示例 — 文件超过 1MB

{ "code": 400, "message": "文件大小超过限制(最大 1MB)", "data": null }

失败响应示例 — Icon 不存在

{ "code": 404, "message": "Icon 不存在", "data": null }

TMDB 刮削服务

wangpan-tmdb-service : 8083
GET /api/movies/recommend 今日推荐(每天固定3部电影)需Token

功能说明

从当前用户的电影库中随机抽取3部电影用于首页展示。

推荐规则:

  • 从 00:00 到 23:59 返回固定的3部电影
  • 当天不管查询多少次都返回相同的结果
  • 第二天 00:00 后重新随机挑选3部
  • 推荐结果缓存在 wp_daily_recommend 表中

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "list": [ { "id": 10, "title": "电影名称1", "posterUrl": "海报URL", "rating": 85, "category": "动作", "tags": "标签1,标签2", "createTime": "2025-01-01T10:00:00" }, { "id": 20, "title": "电影名称2", ... }, { "id": 30, "title": "电影名称3", ... } ], "count": 3 } }

返回字段说明

字段类型说明
listArray推荐的电影列表(最多3部)
list[].idLong电影ID
list[].titleString电影标题
list[].posterUrlString海报URL
list[].ratingInteger评分(0-100)
list[].categoryString分类
list[].tagsString标签
list[].createTimeString创建时间
countInteger实际返回的电影数量(可能小于3)

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }

失败响应示例 — 服务器内部错误

{ "code": 500, "message": "服务器内部错误,请稍后重试", "data": null }
GET /api/movies 电影列表查询(按文件夹分组,每组最多10条)需Token

功能说明

wp_drive_movie 表查询当前用户所有电影,按文件夹分组返回,每个文件夹最多返回 10 条电影数据。支持分页:pageNo 控制文件夹分页。

查询参数

参数类型必填说明
keywordString搜索关键词
statusString电影状态:active(正常)/ deleted(已删除)
scrapeTypeString刮削类型:tmdb / manual / none
sortByString排序字段(如 createTime、rating、title)
sortOrderString排序方向:asc / desc
pageNoint页码,默认 1
pageSizeint每页条数(文件夹数),默认 20

响应字段说明

字段类型说明
totalFoldersInteger文件夹总数
pageNoInteger当前页码
pageSizeInteger每页条数
folderGroups[].folderIdLong文件夹ID
folderGroups[].movieCountInteger该文件夹下返回的电影数量
folderGroups[].movies[].idLong电影ID
folderGroups[].movies[].driveIdLong所属网盘ID
folderGroups[].movies[].folderIdLong所属文件夹ID
folderGroups[].movies[].titleString电影名称
folderGroups[].movies[].originalNameString原始文件名
folderGroups[].movies[].mimeTypeStringMIME类型
folderGroups[].movies[].fileSizeLong文件大小(字节)
folderGroups[].movies[].extensionString文件扩展名
folderGroups[].movies[].descriptionString描述
folderGroups[].movies[].scrapeStatusString刮削状态
folderGroups[].movies[].ratingInteger评分(0-100)
folderGroups[].movies[].tagsString标签
folderGroups[].movies[].castsString主演(逗号分隔,TMDB刮削)
folderGroups[].movies[].directorsString导演(逗号分隔,TMDB刮削)
folderGroups[].movies[].posterUrlString竖版海报URL(2:3 比例)
folderGroups[].movies[].backdropUrlString横版背景图URL(16:9 比例)
folderGroups[].movies[].minioUrlStringMinIO 文件访问URL
folderGroups[].movies[].createTimeString创建时间
folderGroups[].movies[].updateTimeString更新时间

响应示例

{ "code": 200, "message": "查询成功", "data": { "totalFolders": 5, "pageNo": 1, "pageSize": 20, "folderGroups": [ { "folderId": 3, "movieCount": 2, "movies": [ { "id": 1, "driveId": 1, "folderId": 3, "title": "肖申克的救赎", "posterUrl": "http://...:39000/wangpan/posters/1/poster_xxx.jpg", "backdropUrl": "http://...:39000/wangpan/posters/1/backdrop_xxx.jpg", "rating": 93, ... } ] } ] } }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }

失败响应示例 — 参数错误

{ "code": 400, "message": "请求参数错误", "data": null }
GET /api/movies/all 查询所有电影(不分文件夹,直接分页)需Token

功能说明

查询当前用户所有网盘下所有未删除的电影,不进行分类直接分页返回。适用于大数据量场景(1万+),内置5分钟缓存机制,支持关键词搜索和多字段排序。

设计特点:

  • 不进行分类,直接分页查询所有电影
  • 支持关键词搜索(标题模糊匹配)
  • 支持排序(按创建时间、更新时间、评分、标题)
  • 内置5分钟缓存,适用于1万+数据量场景
  • 最大每页100条,防止单次查询数据量过大
  • 每日随机刷新:第1页前20条数据每日随机刷新(无关键词搜索时生效),增加内容变化性,其余数据按排序规则保持不变

每日随机刷新机制:

  • 触发条件:查询第1页且无关键词搜索时
  • 随机数量:前20条数据从所有电影中随机选取
  • 缓存策略:缓存Key包含日期因子,每日自动刷新
  • 其余数据:第21条及以后按指定排序规则返回
  • 关键词搜索时:不触发随机,正常返回搜索结果

查询参数

参数类型必填说明
keywordString搜索关键词(电影标题模糊匹配)
sortByString排序字段:createTime(创建时间)/ updateTime(更新时间)/ rating(评分)/ title(标题)
sortOrderString排序方向:asc(升序)/ desc(降序),默认desc
pageNoint页码,默认 1
pageSizeint每页条数,默认 50,最大 100

响应字段说明

字段类型说明
totalLong电影总数
pagesLong总页数
pageNoLong当前页码
pageSizeLong每页条数
list[].idLong电影ID
list[].driveIdLong所属网盘ID
list[].folderIdLong所属文件夹ID
list[].titleString电影名称
list[].originalNameString原始文件名
list[].mimeTypeStringMIME类型
list[].fileSizeLong文件大小(字节)
list[].extensionString文件扩展名
list[].descriptionString描述
list[].scrapeStatusString刮削状态:pending/scraping/success/failed
list[].ratingInteger评分(0-100)
list[].tagsString标签
list[].castsString主演(逗号分隔,TMDB刮削)
list[].directorsString导演(逗号分隔,TMDB刮削)
list[].posterUrlString竖版海报URL(2:3 比例)
list[].backdropUrlString横版背景图URL(16:9 比例)
list[].minioUrlStringMinIO 文件访问URL
list[].createTimeString创建时间
list[].updateTimeString更新时间

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "total": 12580, "pages": 252, "pageNo": 1, "pageSize": 50, "list": [ { "id": 1, "driveId": 1, "folderId": 3, "title": "肖申克的救赎", "originalName": "The.Shawshank.Redemption.1994.mkv", "fileSize": 8589934592, "description": "一场冤案让银行家安迪入狱,他在肖申克监狱用20年时间挖出一条自由之路...", "scrapeStatus": "success", "rating": 93, "casts": "蒂姆·罗宾斯,摩根·弗里曼", "directors": "弗兰克·德拉邦特", "posterUrl": "http://...:39000/wangpan/posters/1/poster_xxx.jpg", "backdropUrl": "http://...:39000/wangpan/posters/1/backdrop_xxx.jpg", "createTime": "2026-06-20 10:30:00", "updateTime": "2026-06-27 14:20:00" } ] } }

成功响应示例 — 命中缓存

{ "code": 200, "message": "查询成功(缓存)", "data": { "total": 12580, "pages": 252, "pageNo": 1, "pageSize": 50, "list": [...] } }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }

失败响应示例 — 查询失败

{ "code": 500, "message": "查询失败:数据库连接异常", "data": null }

前端调用示例

// 查询第1页,每页50条 fetch('/api/movies/all?pageNo=1&pageSize=50', { headers: { 'Authorization': 'Bearer ' + token } }) .then(res => res.json()) .then(data => { console.log('总数:', data.data.total); console.log('电影列表:', data.data.list); }); // 搜索电影 fetch('/api/movies/all?keyword=肖申克&pageNo=1&pageSize=50', { headers: { 'Authorization': 'Bearer ' + token } }) .then(res => res.json()) .then(data => { console.log('搜索结果:', data.data.list); }); // 按评分降序排序 fetch('/api/movies/all?sortBy=rating&sortOrder=desc&pageNo=1&pageSize=50', { headers: { 'Authorization': 'Bearer ' + token } }) .then(res => res.json()) .then(data => { console.log('高分电影:', data.data.list); });
GET /api/movies/{id} 电影详情查询需Token

功能说明

查询单部电影的完整详情信息,包括基本信息、海报、评分、简介、导演、主演等。

路径参数

参数类型说明
idLong电影ID

响应字段说明

字段类型说明
idLong电影ID
driveIdLong所属网盘ID
folderIdLong所属文件夹ID
titleString电影名称
originalNameString原始文件名
mimeTypeStringMIME类型
fileSizeLong文件大小(字节)
extensionString文件扩展名
descriptionString电影描述
scrapeStatusString刮削状态:pending/scraping/success/failed
ratingInteger评分(0-100)
castsString主演(逗号分隔,TMDB刮削)
directorsString导演(逗号分隔,TMDB刮削)
posterUrlString竖版海报URL(2:3 比例)
backdropUrlString横版背景图URL(16:9 比例)
tagsString标签(逗号分隔)
createTimeString创建时间
updateTimeString更新时间

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "id": 1, "driveId": 1, "folderId": 3, "title": "肖申克的救赎", "originalName": "肖申克的救赎.mp4", "mimeType": "video/mp4", "fileSize": 2147483648, "extension": "mp4", "description": "电影描述", "scrapeStatus": "success", "rating": 93, "casts": "Tim Robbins, Morgan Freeman", "directors": "Frank Darabont", "posterUrl": "http://...:39000/wangpan/posters/1/poster_xxx.jpg", "backdropUrl": "http://...:39000/wangpan/posters/1/backdrop_xxx.jpg", "tags": "剧情,犯罪", "createTime": "2025-01-01T10:00:00", "updateTime": "2025-01-15T10:00:00" } }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在或无权访问", "data": null }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
GET /api/movies/{id}/scrape-status 查询单部电影刮削状态需Token

功能说明

查询单部电影的刮削状态,返回三种状态:刮削中(scraping)、刮削失败(failed)、刮削成功(success)。

路径参数

参数类型说明
idLong电影ID

响应字段说明

字段类型说明
movieIdLong电影ID
titleString电影名称
statusString刮削状态:scraping / failed / success / unknown
messageString状态描述信息
errorString失败原因(仅 status=failed 时返回)
posterUrlString竖版海报URL(仅 status=success 时返回)
backdropUrlString横版背景图URL(仅 status=success 时返回)
ratingInteger评分(仅 status=success 时返回)
categoryString分类(仅 status=success 时返回)
tagsString标签(仅 status=success 时返回)
castsString主演(仅 status=success 时返回)
directorsString导演(仅 status=success 时返回)

响应示例 - 刮削中

{ "code": 200, "message": "查询成功", "data": { "movieId": 1, "title": "电影名称", "status": "scraping", "message": "正在刮削中,请稍候..." } }

响应示例 - 刮削失败

{ "code": 200, "message": "查询成功", "data": { "movieId": 1, "title": "电影名称", "status": "failed", "message": "刮削失败", "error": "TMDB API 请求失败" } }

响应示例 - 刮削成功

{ "code": 200, "message": "查询成功", "data": { "movieId": 1, "title": "电影名称", "status": "success", "message": "刮削成功", "posterUrl": "http://...:39000/wangpan/posters/1/poster_xxx.jpg", "backdropUrl": "http://...:39000/wangpan/posters/1/backdrop_xxx.jpg", "rating": 85, "category": "动作", "tags": "动作,科幻", "casts": "主演1,主演2", "directors": "导演1" } }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在或无权访问", "data": null }
POST /api/movies 创建电影需Token

功能说明

创建新的电影记录,title 为必填字段。

请求体(所有字段可选,title 必填)

{ "title": "电影名称", // 必填 "maintainer": "维护者", // 可选 "status": "active", // 可选:active / deleted "category": "动作", // 可选 "scrapeType": "tmdb", // 可选:tmdb / manual / none "rating": 85, // 可选:0-100 "posterUrl": "海报URL", // 可选 "tags": "标签1,标签2" // 可选 }

成功响应示例

{ "code": 200, "message": "创建成功", "data": { "id": 1, "title": "电影名称", "createTime": "2025-01-01T10:00:00" } }

失败响应示例 — 缺少必填字段

{ "code": 400, "message": "电影名称不能为空", "data": null }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
PUT /api/movies/{id} 更新电影需Token

功能说明

更新指定电影的信息,支持部分更新。

路径参数

参数类型说明
idLong电影ID

请求体(部分更新,所有字段可选)

{ "title": "新电影名称", "maintainer": "新维护者", "status": "active", "category": "新分类", "scrapeType": "tmdb", "rating": 90, "posterUrl": "新海报URL", "tags": "新标签" }

成功响应示例

{ "code": 200, "message": "更新成功", "data": { "id": 1, "title": "新电影名称", "updateTime": "2025-01-15T10:00:00" } }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在", "data": null }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
DELETE /api/movies/{id} 删除电影需Token

功能说明

删除指定的电影记录。

路径参数

参数类型说明
idLong电影ID

成功响应示例

{ "code": 200, "message": "删除成功", "data": null }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在", "data": null }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
POST /api/movies/batch-delete 批量删除电影需Token

功能说明

批量删除多个电影记录。

请求体

{ "ids": [1, 2, 3] }

成功响应示例

{ "code": 200, "message": "批量删除成功", "data": { "deletedCount": 3 } }

失败响应示例 — 参数错误

{ "code": 400, "message": "电影ID列表不能为空", "data": null }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
GET /api/movies/stats 电影统计需Token

功能说明

获取当前用户的电影数量统计,按刮削类型和分类分组。

响应示例

{ "code": 200, "data": { "totalCount": 10, "scrapedCount": 8, "pendingCount": 2 } }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
GET /api/tmdb/key 获取 TMDB API Key需Token

功能说明

获取当前用户配置的 TMDB API Key。

成功响应示例

{ "code": 200, "message": "查询成功", "data": { "tmdbApiKey": "your_api_key_here" } }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
POST /api/tmdb/key 设置 TMDB API Key需Token

请求体

{ "tmdbApiKey": "你的API Key" }
POST /api/tmdb-scraper/scrape 单部电影刮削需Token

功能说明

手动触发单部电影刮削,自动匹配TMDB最佳结果。刮削完成后会更新电影的简介、导演、主演、海报等信息。注意:两次请求需间隔5秒,防止频繁点击。

请求方式

支持两种方式传递 movieId:

1. URL 查询参数?movieId=1

2. 请求体 JSON

{ "movieId": 1 }

响应字段说明

字段类型说明
movieIdLong电影ID
userIdLong用户ID
tmdbIdLongTMDB电影ID
titleString电影标题(来自TMDB)
originalTitleString原始标题(外文)
releaseDateString上映日期
posterPathStringTMDB海报路径
backdropPathStringTMDB背景图路径
localPosterUrlString本地竖版海报URL(MinIO)
localBackdropUrlString本地横版背景图URL(MinIO)
tmdbRatingIntegerTMDB评分(0-100分制)
voteCountInteger投票数
runtimeInteger片长(分钟)
genresString类型(逗号分隔)
directorsString导演(逗号分隔,最多2位)
castsString主演(逗号分隔,最多5位)
originalLanguageString原始语言
productionCountriesString制片国家(逗号分隔)
cacheTimeString缓存时间
statusString缓存状态

成功响应示例

{ "code": 200, "message": "刮削成功", "data": { "movieId": 1, "userId": 1, "tmdbId": 597, "title": "泰坦尼克号", "originalTitle": "Titanic", "releaseDate": "1997-11-18", "posterPath": "/9xjZS2rlVxm8SFx8kPC3JIGCOY.jpg", "backdropPath": "/rTh4K5uw9HypmpGslcKd4QfHl93.jpg", "localPosterUrl": "http://...:39000/wangpan/posters/poster_1_597.jpg", "localBackdropUrl": "http://...:39000/wangpan/posters/backdrop_1_597.jpg", "tmdbRating": 78, "voteCount": 24563, "runtime": 194, "genres": "剧情, 爱情, 灾难", "directors": "James Cameron", "casts": "Leonardo DiCaprio, Kate Winslet, Billy Zane, Kathy Bates, Frances Fisher", "originalLanguage": "en", "productionCountries": "United States of America", "cacheTime": "2026-06-26T16:30:00", "status": "active" } }

失败响应示例 — 操作过于频繁

{ "code": 500, "message": "操作过于频繁,请等待 3 秒后再试", "data": null }

失败响应示例 — 未找到匹配结果

{ "code": 500, "message": "TMDB未找到匹配的电影: xxx", "data": null }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
GET /api/tmdb-scraper/search 搜索TMDB电影(返回所有匹配结果供前端选择)需Token

功能说明

根据电影ID自动获取标题,搜索TMDB返回所有匹配结果。如果查询出多条结果,前端应展示列表让用户选择。

查询参数

参数类型必填说明
movieIdLong电影ID(自动获取标题搜索)
keywordString自定义搜索关键词(不传则使用电影标题)

响应字段说明

字段类型说明
movieIdLong电影ID
keywordString搜索关键词
totalInteger搜索结果总数
list[].tmdbIdLongTMDB电影ID(用于确认刮削)
list[].titleString电影标题
list[].originalTitleString原始标题
list[].releaseDateString上映日期
list[].posterPathString海报图片URL
list[].voteAverageDouble评分

响应示例

{ "code": 200, "message": "查询成功", "data": { "movieId": 23, "keyword": "泰坦尼克号", "total": 3, "list": [ { "tmdbId": 597, "title": "泰坦尼克号", "originalTitle": "Titanic", "releaseDate": "1997-11-18", "posterPath": "https://image.tmdb.org/t/p/w500/xxx.jpg", "voteAverage": 7.9 } ] } }
POST /api/tmdb-scraper/confirm 确认刮削(用户选择TMDB结果后执行)需Token

功能说明

用户从搜索结果中选择一部电影后,调用此接口执行刮削。刮削完成后会更新电影的简介、导演、主演、海报等信息。注意:两次请求需间隔5秒,防止频繁点击。

请求体

{ "movieId": 23, "tmdbId": 597 }

响应字段说明

字段类型说明
movieIdLong电影ID
userIdLong用户ID
tmdbIdLongTMDB电影ID
titleString电影标题(来自TMDB)
originalTitleString原始标题(外文)
releaseDateString上映日期
posterPathStringTMDB海报路径
backdropPathStringTMDB背景图路径
localPosterUrlString本地竖版海报URL(MinIO)
localBackdropUrlString本地横版背景图URL(MinIO)
tmdbRatingIntegerTMDB评分(0-100分制)
voteCountInteger投票数
runtimeInteger片长(分钟)
genresString类型(逗号分隔)
directorsString导演(逗号分隔,最多2位)
castsString主演(逗号分隔,最多5位)
originalLanguageString原始语言
productionCountriesString制片国家(逗号分隔)
cacheTimeString缓存时间
statusString缓存状态

成功响应示例

{ "code": 200, "message": "刮削成功", "data": { "movieId": 23, "userId": 1, "tmdbId": 597, "title": "泰坦尼克号", "originalTitle": "Titanic", "releaseDate": "1997-11-18", "posterPath": "/9xjZS2rlVxm8SFx8kPC3JIGCOY.jpg", "backdropPath": "/rTh4K5uw9HypmpGslcKd4QfHl93.jpg", "localPosterUrl": "http://...:39000/wangpan/posters/poster_23_597.jpg", "localBackdropUrl": "http://...:39000/wangpan/posters/backdrop_23_597.jpg", "tmdbRating": 78, "voteCount": 24563, "runtime": 194, "genres": "剧情, 爱情, 灾难", "directors": "James Cameron", "casts": "Leonardo DiCaprio, Kate Winslet, Billy Zane, Kathy Bates, Frances Fisher", "originalLanguage": "en", "productionCountries": "United States of America", "cacheTime": "2026-06-26T16:30:00", "status": "active" } }

失败响应示例 — 操作过于频繁

{ "code": 500, "message": "操作过于频繁,请等待 3 秒后再试", "data": null }

失败响应示例 — 电影不存在

{ "code": 404, "message": "电影不存在", "data": null }

失败响应示例 — 未登录或 Token 无效

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
POST /api/tmdb-scraper/batch-scrape 批量刮削需Token

请求体(movieIds 可选)

{ "movieIds": [1, 2, 3] } // 可选:不传则自动查找需要刮削的电影

响应示例

{ "code": 200, "message": "批量刮削完成", "data": { "successCount": 2, "failCount": 0, "total": 2 } }
GET /api/tmdb-scraper/caches 查询缓存列表需Token

查询参数

参数默认值说明
pageNo1页码
pageSize20每页条数
GET /api/tmdb-scraper/stats 刮削统计需Token

响应示例

{ "code": 200, "data": { "totalCaches": 5, "activeCaches": 3, "expiredCaches": 2, "userId": 2 } }
POST /api/tmdb-scraper/trigger 手动触发定时任务需Token

功能说明

手动触发 TMDB 定时扫描任务,查找需要刮削的电影并执行刮削。无需请求体。

响应示例

{ "code": 200, "message": "扫描任务已触发", "data": { "message": "扫描任务已触发", "triggeredBy": 2, "triggerType": "manual" } }
GET /api/tmdb-scraper/scraper-logs 查询刮削日志(分页)需Token

功能说明

分页查询定时任务执行记录,返回每条记录的汇总信息和详细结果(包含每部电影的刮削结果和失败原因)。

查询参数

参数类型必填说明
pageNoInteger页码,默认 1
pageSizeInteger每页条数,默认 20,最大 50

响应字段说明

字段类型说明
totalLong总记录数
pageNoInteger当前页码
pageSizeInteger每页条数
list[].idLong日志ID
list[].trigger_typeString触发方式:auto(定时自动)/ manual(手动触发)
list[].statusString执行状态:success / partial / failed / skipped
list[].total_processedInteger总处理电影数
list[].total_successInteger成功刮削数
list[].total_skippedInteger跳过数
list[].total_failedInteger失败数
list[].user_countInteger涉及用户数
list[].duration_msLong执行耗时(毫秒)
list[].start_timeString开始时间
list[].end_timeString结束时间
list[].error_messageString错误信息(如有)
list[].detailsArray详细执行结果(解析后的JSON)
list[].details[].usernameString用户名
list[].details[].userIdLong用户ID
list[].details[].processedInteger该用户处理的电影数
list[].details[].successInteger该用户成功刮削数
list[].details[].skippedInteger该用户跳过数
list[].details[].failedInteger该用户失败数
list[].details[].messagesArray失败详情列表(格式:电影名称: 失败原因)

响应示例

{ "code": 200, "message": "查询成功", "data": { "total": 10, "pageNo": 1, "pageSize": 20, "list": [ { "id": 1, "trigger_type": "auto", "status": "partial", "total_processed": 5, "total_success": 4, "total_skipped": 0, "total_failed": 1, "user_count": 1, "duration_ms": 12500, "start_time": "2026-06-27T10:00:00", "end_time": "2026-06-27T10:00:12", "error_message": null, "details": [ { "username": "testuser", "userId": 1, "processed": 5, "success": 4, "skipped": 0, "failed": 1, "messages": [ "泰坦尼克号: TMDB未找到匹配的电影" ] } ] } ] } }
GET /api/tmdb-scraper/scraper-logs/{id} 查询单条刮削日志详情需Token

功能说明

查询单条刮削日志的详细信息,包括每个用户的刮削结果、失败电影列表及原因。

路径参数

参数类型说明
idLong日志ID

响应字段说明

字段类型说明
idLong日志ID
trigger_typeString触发方式:auto(定时自动)/ manual(手动触发)
statusString执行状态:success / partial / failed / skipped
total_processedInteger总处理电影数
total_successInteger成功刮削数
total_skippedInteger跳过数
total_failedInteger失败数
user_countInteger涉及用户数
duration_msLong执行耗时(毫秒)
start_timeString开始时间
end_timeString结束时间
error_messageString错误信息(如有)
detailsArray详细执行结果(解析后的JSON)
details[].usernameString用户名
details[].userIdLong用户ID
details[].processedInteger该用户处理的电影数
details[].successInteger该用户成功刮削数
details[].skippedInteger该用户跳过数
details[].failedInteger该用户失败数
details[].messagesArray失败详情列表(格式:电影名称: 失败原因)

响应示例

{ "code": 200, "message": "查询成功", "data": { "id": 1, "trigger_type": "auto", "status": "partial", "total_processed": 5, "total_success": 4, "total_skipped": 0, "total_failed": 1, "user_count": 1, "duration_ms": 12500, "start_time": "2026-06-27T10:00:00", "end_time": "2026-06-27T10:00:12", "error_message": null, "details": [ { "username": "testuser", "userId": 1, "processed": 5, "success": 4, "skipped": 0, "failed": 1, "messages": [ "泰坦尼克号: TMDB未找到匹配的电影", "阿凡达: TMDB API请求失败,请检查API Key是否有效" ] } ] } }

失败响应示例 — 日志不存在

{ "code": 404, "message": "日志不存在", "data": null }
DELETE /api/tmdb-scraper/cache/{movieId} 删除缓存需Token

路径参数

参数类型说明
movieIdLong电影ID

功能说明

删除指定电影的 TMDB 刮削缓存,下次刮削时将重新从 TMDB 获取数据。

响应示例

{ "code": 200, "message": "缓存删除成功", "data": null }

OpenList 用户级连接管理

wangpan-drive-service : 8082 · OpenListController / DriveController

功能说明

每个用户可以绑定自己的 OpenList 实例(地址、账号、密码),互不干扰。 敏感信息使用 Jasypt 加密存储,Token 缓存到数据库避免重复登录。 OpenList 负责文件操作(同步、播放),TMDB 刮削使用当前用户的 API Key。

前端
Gateway:8888
Drive:8082
用户A的OpenList
UC网盘
用户B的OpenList
阿里云盘

使用流程

1. 前端调用 POST /api/openlist/test 测试 OpenList 连接(可选)
2. 前端调用 POST /api/openlist/bind 绑定 OpenList(传入地址、账号、密码)
3. 绑定成功后后端自动异步同步:递归遍历 OpenList 目录树,同步文件夹层级和视频文件到数据库
4. 也可手动调用 POST /api/openlist/sync 触发全量同步
5. 同步后电影可进行 TMDB 刮削,播放视频自动使用用户绑定的 OpenList 连接
6. 后端每 24 小时自动检测 OpenList 心跳,记录连接状态
7. TMDB 刮削使用当前用户的 API Key(与 OpenList 连接隔离)

POST /api/openlist/test 测试 OpenList 连接(不保存) 需Token

请求头

Authorization: Bearer <token> Content-Type: application/json

请求体

{ "openlistUrl": "http://www.example.com:3000", "openlistUsername": "用户名", "openlistPassword": "密码" }

字段说明

字段类型必填说明
openlistUrlStringOpenList 服务地址(如 http://example.com:13000)
openlistUsernameStringOpenList 账号
openlistPasswordStringOpenList 密码

成功响应示例

{ "code": 200, "message": "连接成功", "data": null }

失败响应示例

{ "code": 500, "message": "连接失败:Invalid username or password", "data": null }
POST /api/openlist/bind 绑定 OpenList 连接 需Token

请求头

Authorization: Bearer <token> Content-Type: application/json

请求体

{ "openlistUrl": "http://example.com:13000", "openlistUsername": "your_username", "openlistPassword": "your_password" }

字段说明

字段类型必填说明
openlistUrlStringOpenList 服务地址
openlistUsernameStringOpenList 账号
openlistPasswordStringOpenList 密码

功能说明

验证连接 → 保存配置(敏感信息 Jasypt 加密) → 缓存 Token(24小时有效期)。
每个用户只能绑定一个 OpenList 实例,重复绑定会更新配置。
绑定成功后自动触发异步同步:递归遍历 OpenList 目录树,同步文件夹层级和视频文件到数据库。
同步只处理视频格式文件(mp4, mkv, avi 等),非视频文件自动跳过。

成功响应示例

{ "code": 200, "message": "绑定成功,正在后台同步文件...", "data": null }

失败响应示例

{ "code": 500, "message": "绑定失败:连接失败:Invalid username or password", "data": null }
DELETE /api/openlist/unbind 解绑 OpenList 连接 需Token

请求头

Authorization: Bearer <token>

功能说明

删除当前用户的 OpenList 连接配置(包括加密的 URL、账号、密码和 Token 缓存)。
解绑后同步文件和播放链接接口将不可用。

成功响应示例

{ "code": 200, "message": "解绑成功", "data": null }

失败响应示例

{ "code": 500, "message": "解绑失败:未找到绑定记录", "data": null }
GET /api/openlist/info 查询当前用户 OpenList 连接信息 需Token

请求头

Authorization: Bearer <token>

功能说明

返回脱敏后的连接信息(URL、用户名均脱敏显示,不返回明文密码)。
会实际调用 OpenList 登录接口验证当前连接是否可用,返回 connectedconnectMessage 字段。
未绑定时返回 null

成功响应示例(已绑定)

{ "code": 200, "message": "操作成功", "data": { "id": 1, "openlistUrl": "http://ld***:13000", "openlistUsername": "dj***22", "storageId": "1", "mountPath": "/uc", "status": "active", "tokenValid": true, "connected": true, "connectMessage": "连接成功", "lastHeartbeatTime": "2026-06-27T15:00:00", "heartbeatStatus": "success", "heartbeatError": null, "createTime": "2026-06-26T15:00:00", "updateTime": "2026-06-26T15:00:00" } }

响应字段说明

字段说明
openlistUrlOpenList 服务地址(脱敏显示,如 http://ld***:13000)
openlistUsernameOpenList 账号(脱敏显示,如 dj***22)
storageIdOpenList 存储 ID(用户在 OpenList 后台配置)
mountPath挂载路径(如 /uc)
status状态:active / disabled
tokenValid本地缓存的 Token 是否有效(true 表示未过期)
connected实时连接测试结果:true 表示当前账号密码可成功连接 OpenList
connectMessage实时连接测试结果描述,如 "连接成功" 或 "连接失败:Invalid username or password"
lastHeartbeatTime最后一次定时心跳检测时间(每 24 小时自动检测一次)
heartbeatStatus最近一次心跳检测结果:success / failed
heartbeatError心跳检测失败原因(成功时为 null)

成功响应示例(未绑定)

{ "code": 200, "message": "操作成功", "data": null }
POST /api/openlist/sync 手动触发 OpenList 文件同步 需Token

请求头

Authorization: Bearer <token>

功能说明

从 OpenList 根目录开始递归遍历目录树,同步文件夹层级和视频文件到本地数据库。
同步策略
  1. 自动为用户创建/复用关联 OpenList 的网盘(Drive)
  2. 递归遍历 OpenList 目录树,在本地创建对应的文件夹(DriveFolder)
  3. 只同步视频格式文件(mp4, mkv, avi, mov, wmv, flv, webm 等),非视频文件自动跳过
  4. 视频文件创建为电影记录(DriveMovie),storageType=openlist
  5. 不含视频文件的空文件夹也会被跳过
前置条件:用户已通过 /api/openlist/bind 绑定 OpenList 连接。
注意:绑定 OpenList 时已自动触发异步同步,此接口用于手动重新同步。

成功响应示例

{ "code": 200, "message": "同步完成", "data": { "driveCount": 1, "folderCount": 10, "movieCount": 50, "skippedFileCount": 5, "skippedFolderCount": 2, "errorCount": 0, "errors": [] } }

响应字段说明

字段类型说明
driveCountint关联网盘数量(通常为 1)
folderCountint本次同步创建的文件夹数量
movieCountint本次同步创建的视频文件(电影)数量
skippedFileCountint跳过的非视频文件数量
skippedFolderCountint跳过的不含视频的文件夹数量
errorCountint同步过程中的错误数量
errorsArray错误详情列表(字符串数组)

失败响应示例

{ "code": 500, "message": "同步失败:用户未绑定 OpenList,请先绑定", "data": null }
POST /api/drive/{driveId}/folders/{folderId}/sync-files 从 OpenList 同步文件夹文件 需Token

请求头

Authorization: Bearer <token>

路径参数

参数类型说明
driveIdLong网盘 ID
folderIdLong文件夹 ID(0 表示根目录)

功能说明

使用当前用户绑定的 OpenList 连接,获取指定路径下的文件列表,自动创建数据库中缺失的电影记录。
已存在的文件不会重复创建。同步后文件可进行 TMDB 刮削。
前置条件:用户已通过 /api/openlist/bind 绑定 OpenList 连接。

成功响应示例

{ "code": 200, "message": "同步成功", "data": { "syncCount": 5, "totalFiles": 10 } }

响应字段说明

字段说明
syncCount本次新增的电影记录数量
totalFilesOpenList 中该路径下的文件总数

失败响应示例

{ "code": 500, "message": "同步失败:用户未绑定 OpenList,请先在设置中绑定 OpenList 连接", "data": null }
GET /api/drive/{driveId}/movies/{movieId}/play-url 获取电影真实播放链接 需Token

请求头

Authorization: Bearer <token>

路径参数

参数类型说明
driveIdLong网盘 ID
movieIdLong电影 ID

功能说明

使用当前用户绑定的 OpenList 连接,获取文件的真实下载地址,返回的 URL 可直接用于视频播放器播放。
仅支持 storageTypeopenlist 的电影记录。
Token 过期时自动刷新,对用户透明。

成功响应示例

{ "code": 200, "message": "获取成功", "data": { "playUrl": "https://uc网盘真实下载地址/xxx.mp4", "fileName": "阿凡达.mp4", "fileSize": 1073741824 } }

响应字段说明

字段说明
playUrl真实播放/下载地址,可直接用于视频播放器
fileName文件名
fileSize文件大小(字节)

失败响应示例

{ "code": 500, "message": "获取失败:该电影不是 OpenList 管理的文件", "data": null }
GET /api/drive/{driveId}/folders/{folderId}/movies 查看电影列表(含 OpenList 信息) 需Token

请求头

Authorization: Bearer <token>

路径参数

参数类型说明
driveIdLong网盘 ID
folderIdLong文件夹 ID(0 表示根目录)

成功响应示例

{ "code": 200, "data": { "list": [ { "id": 1, "title": "阿凡达.mp4", "openlistFilePath": "/uc/阿凡达.mp4", "storageType": "openlist", "fileSize": 1073741824, "mimeType": "video/mp4" } ], "total": 10, "page": 1, "pageSize": 20 } }

OpenList 相关字段说明

字段说明
openlistFilePathOpenList 文件路径(如 "/uc/阿凡达.mp4"),由系统根据用户绑定的挂载路径自动生成
storageType存储类型:metadata(仅元数据)/ openlist(OpenList 管理)
fileSize文件大小(字节),同步时从 OpenList 获取

前端调用示例

1. 绑定 OpenList

// 先测试连接 const testResp = await fetch('/api/openlist/test', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ openlistUrl: 'http://example.com:13000', openlistUsername: 'your_username', openlistPassword: 'your_password' }) }); // 测试通过后绑定 const bindResp = await fetch('/api/openlist/bind', { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ openlistUrl: 'http://example.com:13000', openlistUsername: 'your_username', openlistPassword: 'your_password' }) });

2. 查询连接信息

const resp = await fetch('/api/openlist/info', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await resp.json(); if (data.data) { console.log('已绑定:', data.data.openlistUrl); console.log('Token有效:', data.data.tokenValid); } else { console.log('未绑定 OpenList'); }

3. 手动同步文件

// 绑定后已自动触发异步同步,也可手动重新同步 const resp = await fetch('/api/openlist/sync', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); const data = await resp.json(); console.log(`同步了 ${data.data.folderCount} 个文件夹, ${data.data.movieCount} 个视频`); if (data.data.errorCount > 0) { console.warn('同步错误:', data.data.errors); }

4. 查询连接状态

const resp = await fetch('/api/openlist/info', { headers: { 'Authorization': `Bearer ${token}` } }); const data = await resp.json(); if (data.data) { console.log('地址:', data.data.openlistUrl); console.log('实时连接:', data.data.connected ? '正常' : '异常'); console.log('连接信息:', data.data.connectMessage); console.log('心跳状态:', data.data.heartbeatStatus); } else { console.log('未绑定 OpenList'); }

5. 播放视频

const resp = await fetch(`/api/drive/${driveId}/movies/${movieId}/play-url`, { headers: { 'Authorization': `Bearer ${token}` } }); const data = await resp.json(); videoPlayer.src = data.data.playUrl; // 直接设置视频源 videoPlayer.play();

6. 解绑 OpenList

const resp = await fetch('/api/openlist/unbind', { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); const data = await resp.json(); console.log(data.message); // "解绑成功"

通用响应格式

{ "code": 200, "message": "操作结果描述", "data": { ... } }

状态码说明

状态码说明
200请求成功
400请求参数错误
401未授权 / 令牌无效
403权限不足
404资源不存在
500服务器内部错误

认证说明

除以下 3 个接口 外,所有接口都需要在请求头中携带访问令牌:

无需 Token 的接口:
  POST /api/login  用户登录
  POST /api/register  用户注册
  GET /api/token/validate?token=xxx  令牌验证(token 通过 URL 参数传递)

需要 Token 的接口(带 需Token 标识):
  在请求头中添加:Authorization: Bearer <token>