服务架构图
前端
→
Gateway:8888
→
User:8081
前端
→
Gateway
→
Drive:8082
前端
→
Gateway
→
Drive:8082
→
OpenList:13000
→
各网盘
路由规则
| 请求路径 | 转发服务 | 说明 |
| /api/login, /api/register | user-service | 登录注册 |
| /api/token/**, /api/login-logs/** | user-service | 令牌/日志 |
| /api/users/** | user-service | 用户管理 |
| /api/profile/** | user-service | 个人资料 |
| /api/drive/** | drive-service | 网盘管理 |
| /api/drive/**/sync-files | drive-service | OpenList 文件同步 |
| /api/drive/**/play-url | drive-service | OpenList 播放链接 |
| /api/icon/** | drive-service | Icon管理 |
| /api/movies/** | tmdb-service | 电影管理 |
| /api/tmdb/**, /api/tmdb-scraper/** | tmdb-service | TMDB刮削 |
用户认证服务
wangpan-user-service : 8081
请求体
{
"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 | 普通用户 | 仅访问自己的数据(如日志只看自己的) |
| 1 | VIP 用户 | 普通用户 + 更多存储空间 |
| 2 | 管理员 | 可查看所有用户数据(如全部日志) |
请求体
{
"username": "用户名",
"password": "密码"
}
成功响应示例
{
"code": 200,
"message": "注册成功",
"data": {
"id": 10,
"username": "newuser"
}
}
失败响应示例 — 用户名已存在
{
"code": 409,
"message": "用户名已被占用",
"data": null
}
失败响应示例 — 参数缺失
{
"code": 400,
"message": "用户名和密码不能为空",
"data": null
}
请求头
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
}
权限说明
| 用户类型 | 可见范围 |
| 普通用户(userType=0/1) | 仅自己的登录记录 |
| 管理员(userType=2) | 所有用户的登录记录 |
查询参数
| 参数 | 类型 | 必填 | 说明 |
| username | String | 否 | 按用户名模糊筛选(管理员可用) |
| result | String | 否 | 登录结果:success / fail |
| startDate | String | 否 | 开始日期(含),格式:yyyy-MM-dd,如 2026-06-01 |
| endDate | String | 否 | 结束日期(含),格式:yyyy-MM-dd,如 2026-06-10 |
| pageNo | int | 否 | 页码,默认 1 |
| pageSize | int | 否 | 每页条数,默认 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)
权限说明
| 角色 | 访问权限 |
| 普通/VIP 用户 | 403 权限不足 |
| 管理员 | 可查看所有用户列表 |
查询参数
| 参数 | 类型 | 必填 | 说明 |
| username | String | 否 | 用户名模糊搜索 |
| status | int | 否 | 状态:0=禁用, 1=正常, 2=锁定 |
| userType | int | 否 | 角色:0=普通, 1=VIP, 2=管理员 |
| pageNo | int | 否 | 页码,默认 1 |
| pageSize | int | 否 | 每页条数,默认 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
}
请求体
{
"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
}
请求体(部分更新,只传需要修改的字段)
{
"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
}
路径参数
| 参数 | 类型 | 说明 |
| userId | Long | 要删除的用户 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
功能说明
获取当前登录用户的基本信息(不含密码等敏感字段)。
成功响应示例
{
"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
}
功能说明
修改当前登录用户的用户名。
校验规则:
- 用户名不能为空
- 新用户名不能与当前用户名相同
- 新用户名不能被其他用户占用
请求体
{
"username": "新用户名"
}
成功响应示例
{
"code": 200,
"message": "用户名修改成功",
"data": null
}
失败响应示例 — 用户名已存在
{
"code": 409,
"message": "用户名已被其他用户使用",
"data": null
}
失败响应示例 — 参数错误
{
"code": 400,
"message": "新用户名不能为空或与当前用户名相同",
"data": null
}
功能说明
修改当前登录用户的头像。
流程:
- 接收 Base64 编码的图片数据
- 解析 MIME 类型和 Base64 内容
- 上传到 MinIO(路径:avatar/{userId}_{timestamp}.{ext})
- 更新数据库中的 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
请求体
{
"name": "网盘名称",
"description": "网盘描述",
"logoData": "data:image/png;base64,iVBOR..."
}
参数说明
| 参数 | 类型 | 必填 | 说明 |
| name | String | 是 | 网盘名称 |
| description | String | 否 | 网盘描述 |
| logoData | String | 否 | Logo图片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
}
查询参数
| 参数 | 默认值 | 说明 |
| pageNo | 1 | 页码 |
| pageSize | 20 | 每页条数 |
响应字段
| 字段 | 类型 | 说明 |
| id | Long | 网盘ID |
| name | String | 网盘名称 |
| description | String | 网盘描述 |
| logoUrl | String | 网盘Logo图片链接(MinIO存储地址) |
| folderCount | Integer | 文件夹数量 |
| fileCount | Integer | 文件数量 |
| totalSize | Long | 总大小(字节) |
| status | String | 状态 |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |
成功响应示例
{
"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
}
响应字段
| 字段 | 类型 | 说明 |
| id | Long | 网盘ID |
| name | String | 网盘名称 |
| description | String | 网盘描述 |
| logoUrl | String | 网盘Logo图片链接(MinIO存储地址) |
| folderCount | Integer | 文件夹数量 |
| fileCount | Integer | 文件数量 |
| totalSize | Long | 总大小(字节) |
| status | String | 状态 |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |
成功响应示例
{
"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
}
请求体
{
"name": "新网盘名称",
"description": "新描述",
"logoData": "data:image/png;base64,iVBOR..."
}
参数说明
| 参数 | 类型 | 必填 | 说明 |
| name | String | 否 | 网盘名称 |
| description | String | 否 | 网盘描述 |
| logoData | String | 否 | 新的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
}
成功响应示例
{
"code": 200,
"message": "删除成功",
"data": null
}
失败响应示例 — 网盘不存在
{
"code": 404,
"message": "网盘不存在",
"data": null
}
失败响应示例 — 权限不足
{
"code": 403,
"message": "无权删除该网盘",
"data": null
}
请求体
{
"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
}
成功响应示例
{
"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
}
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘ID |
| folderId | Long | 文件夹ID |
响应字段
| 字段 | 类型 | 说明 |
| id | Long | 文件夹ID |
| driveId | Long | 所属网盘ID |
| parentId | Long | 父文件夹ID(0表示根目录) |
| name | String | 文件夹名称 |
| description | String | 文件夹描述 |
| depth | Integer | 层级深度 |
| childFolderCount | Integer | 子文件夹数量 |
| fileCount | Integer | 文件数量 |
| totalSize | Long | 总大小(字节) |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |
成功响应示例
{
"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
}
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘ID |
| folderId | Long | 文件夹ID |
请求体(部分更新)
{
"name": "新文件夹名称",
"description": "新描述",
"parentId": 0
}
参数说明
| 参数 | 类型 | 必填 | 说明 |
| name | String | 否 | 文件夹名称 |
| description | String | 否 | 文件夹描述 |
| parentId | Long | 否 | 父文件夹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
}
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘ID |
| folderId | Long | 文件夹ID |
说明
删除文件夹前会检查文件夹是否为空,如果包含子文件夹或电影文件则拒绝删除。
错误码
| code | 说明 |
| 400 | 文件夹不为空或数据库操作失败 |
| 401 | 未登录或Token无效 |
| 403 | 无权访问该文件夹 |
请求体
{
"title": "文件名.mp4", // 必填,文件名(同时作为电影标题)
"folderId": 0 // 可选,默认0(根目录)
}
参数说明
| 参数 | 类型 | 必填 | 说明 |
| title | String | 是 | 文件名(同时作为电影标题,后续通过 TMDB 刮削补充信息) |
| folderId | Long | 否 | 目标文件夹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
}
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘ID |
| folderId | Long | 文件夹ID(0表示根目录) |
查询参数
| 参数 | 默认值 | 说明 |
| pageNo | 1 | 页码 |
| pageSize | 20 | 每页条数(最大100) |
响应字段
| 字段 | 类型 | 说明 |
| list[].id | Long | 电影ID |
| list[].title | String | 电影名称 |
| list[].originalName | String | 原始文件名 |
| list[].mimeType | String | MIME类型 |
| list[].fileSize | Long | 文件大小(字节) |
| list[].extension | String | 文件扩展名 |
| list[].minioUrl | String | MinIO文件访问URL |
| list[].rating | Integer | 评分 |
| list[].tags | String | 标签 |
| total | Integer | 总数 |
| pageNo | Integer | 当前页码 |
| pageSize | Integer | 每页条数 |
成功响应示例
{
"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
}
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘ID |
| movieId | Long | 电影ID |
响应字段
| 字段 | 类型 | 说明 |
| id | Long | 电影ID |
| driveId | Long | 所属网盘ID |
| folderId | Long | 所属文件夹ID |
| title | String | 电影名称 |
| mimeType | String | MIME类型 |
| fileSize | Long | 文件大小(字节) |
| extension | String | 文件扩展名 |
| description | String | 描述 |
| maintainer | String | 维护者 |
| status | String | 状态 |
| scrapeType | String | 刮削类型 |
| scrapeStatus | String | 刮削状态 |
| rating | Integer|null | 评分(刮削成功时返回) |
| tags | String | 标签 |
| casts | String|null | 主演(刮削成功时返回) |
| directors | String|null | 导演(刮削成功时返回) |
| posterUrl | String|null | 海报URL(刮削成功时返回) |
| backdropUrl | String|null | 背景图URL(刮削成功时返回) |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |
成功响应示例(刮削成功)
{
"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
}
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘ID |
| movieId | Long | 电影ID |
请求体(部分更新,所有字段可选)
{
"title": "新电影名称",
"description": "新描述",
"rating": 90,
"tags": "新标签",
"posterUrl": "海报URL",
"backdropUrl": "背景图URL",
"casts": "主演列表",
"directors": "导演列表",
"resourceLibrary": "资源库来源",
"maintainer": "维护者"
}
参数说明
| 参数 | 类型 | 说明 |
| title | String | 电影名称 |
| description | String | 描述 |
| rating | Integer | 评分 0-100 |
| tags | String | 标签 |
| posterUrl | String | 海报URL |
| backdropUrl | String | 背景图URL |
| casts | String | 主演(逗号分隔) |
| directors | String | 导演(逗号分隔) |
| resourceLibrary | String | 资源库来源 |
| maintainer | String | 维护者 |
说明
用于 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
}
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘ID |
| movieId | Long | 电影ID |
说明
逻辑删除(软删除),删除后自动更新网盘和文件夹的统计信息。
成功响应示例
{
"code": 200,
"message": "删除成功",
"data": null
}
失败响应示例 — 电影不存在
{
"code": 404,
"message": "电影不存在",
"data": null
}
失败响应示例 — 权限不足
{
"code": 403,
"message": "无权删除该电影",
"data": null
}
功能说明
查询当前用户所有网盘下所有文件夹中的电影,按标题(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
}
}
返回字段说明
| 字段 | 类型 | 说明 |
| duplicateGroups | Array | 重复电影分组列表 |
| duplicateGroups[].title | String | 重复的电影标题 |
| duplicateGroups[].count | Integer | 该标题下的重复数量 |
| duplicateGroups[].movies | Array | 该标题下的所有电影记录 |
| duplicateGroups[].movies[].id | Long | 电影ID(可用于删除接口) |
| duplicateGroups[].movies[].folderPath | String | 所在文件夹完整路径,如"网盘名/文件夹1/文件夹2" |
| totalGroups | Integer | 重复组总数 |
| totalDuplicates | Integer | 涉及重复的电影总数 |
错误码
| code | 说明 |
| 401 | 未登录或 Token 无效 |
| 500 | 服务器内部错误 |
Icon 管理服务
wangpan-drive-service : 8082
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
| name | String | 是 | Icon 名称(最大100字符) |
| url | String | 条件必填 | 外网链接 URL(最大500字符,linkType为external或both时必填) |
| internalUrl | String | 条件必填 | 内网链接 URL(最大500字符,linkType为internal或both时必填) |
| linkType | String | 否 | 链接类型:external(仅外网,默认)/ internal(仅内网)/ both(内外网) |
| description | String | 否 | Icon 描述(最大500字符) |
| sortOrder | Integer | 否 | 排序值,数字越小越靠前,默认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"
}
}
功能说明
查询当前登录用户的所有 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
}
}
成功响应(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
}
请求体(JSON,所有字段可选)
| 字段 | 类型 | 说明 |
| name | String | Icon 名称 |
| url | String | 外网链接 |
| internalUrl | String | 内网链接 |
| linkType | String | 链接类型:external / internal / both |
| description | String | Icon 描述 |
| sortOrder | Integer | 排序值 |
请求示例
{
"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 无效 |
| 500 | Icon 不存在或无权修改 |
成功响应(200)
{
"code": 200,
"message": "删除成功",
"data": null
}
错误码
| code | 说明 |
| 401 | 未登录或 Token 无效 |
| 500 | Icon 不存在或无权删除 |
功能说明
上传自定义图标图片替换自动抓取的 logo。上传后会自动删除旧的 MinIO 图片,防止硬盘空间浪费。系统每 24 小时自动清理无用图片。
请求格式
multipart/form-data
| 字段 | 类型 | 必填 | 说明 |
| file | File | 是 | 图片文件(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
功能说明
从当前用户的电影库中随机抽取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
}
}
返回字段说明
| 字段 | 类型 | 说明 |
| list | Array | 推荐的电影列表(最多3部) |
| list[].id | Long | 电影ID |
| list[].title | String | 电影标题 |
| list[].posterUrl | String | 海报URL |
| list[].rating | Integer | 评分(0-100) |
| list[].category | String | 分类 |
| list[].tags | String | 标签 |
| list[].createTime | String | 创建时间 |
| count | Integer | 实际返回的电影数量(可能小于3) |
失败响应示例 — 未登录或 Token 无效
{
"code": 401,
"message": "未登录或 Token 已过期",
"data": null
}
失败响应示例 — 服务器内部错误
{
"code": 500,
"message": "服务器内部错误,请稍后重试",
"data": null
}
功能说明
从 wp_drive_movie 表查询当前用户所有电影,按文件夹分组返回,每个文件夹最多返回 10 条电影数据。支持分页:pageNo 控制文件夹分页。
查询参数
| 参数 | 类型 | 必填 | 说明 |
| keyword | String | 否 | 搜索关键词 |
| status | String | 否 | 电影状态:active(正常)/ deleted(已删除) |
| scrapeType | String | 否 | 刮削类型:tmdb / manual / none |
| sortBy | String | 否 | 排序字段(如 createTime、rating、title) |
| sortOrder | String | 否 | 排序方向:asc / desc |
| pageNo | int | 否 | 页码,默认 1 |
| pageSize | int | 否 | 每页条数(文件夹数),默认 20 |
响应字段说明
| 字段 | 类型 | 说明 |
| totalFolders | Integer | 文件夹总数 |
| pageNo | Integer | 当前页码 |
| pageSize | Integer | 每页条数 |
| folderGroups[].folderId | Long | 文件夹ID |
| folderGroups[].movieCount | Integer | 该文件夹下返回的电影数量 |
| folderGroups[].movies[].id | Long | 电影ID |
| folderGroups[].movies[].driveId | Long | 所属网盘ID |
| folderGroups[].movies[].folderId | Long | 所属文件夹ID |
| folderGroups[].movies[].title | String | 电影名称 |
| folderGroups[].movies[].originalName | String | 原始文件名 |
| folderGroups[].movies[].mimeType | String | MIME类型 |
| folderGroups[].movies[].fileSize | Long | 文件大小(字节) |
| folderGroups[].movies[].extension | String | 文件扩展名 |
| folderGroups[].movies[].description | String | 描述 |
| folderGroups[].movies[].scrapeStatus | String | 刮削状态 |
| folderGroups[].movies[].rating | Integer | 评分(0-100) |
| folderGroups[].movies[].tags | String | 标签 |
| folderGroups[].movies[].casts | String | 主演(逗号分隔,TMDB刮削) |
| folderGroups[].movies[].directors | String | 导演(逗号分隔,TMDB刮削) |
| folderGroups[].movies[].posterUrl | String | 竖版海报URL(2:3 比例) |
| folderGroups[].movies[].backdropUrl | String | 横版背景图URL(16:9 比例) |
| folderGroups[].movies[].minioUrl | String | MinIO 文件访问URL |
| folderGroups[].movies[].createTime | String | 创建时间 |
| folderGroups[].movies[].updateTime | String | 更新时间 |
响应示例
{
"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
}
功能说明
查询当前用户所有网盘下所有未删除的电影,不进行分类直接分页返回。适用于大数据量场景(1万+),内置5分钟缓存机制,支持关键词搜索和多字段排序。
设计特点:
- 不进行分类,直接分页查询所有电影
- 支持关键词搜索(标题模糊匹配)
- 支持排序(按创建时间、更新时间、评分、标题)
- 内置5分钟缓存,适用于1万+数据量场景
- 最大每页100条,防止单次查询数据量过大
- 每日随机刷新:第1页前20条数据每日随机刷新(无关键词搜索时生效),增加内容变化性,其余数据按排序规则保持不变
每日随机刷新机制:
- 触发条件:查询第1页且无关键词搜索时
- 随机数量:前20条数据从所有电影中随机选取
- 缓存策略:缓存Key包含日期因子,每日自动刷新
- 其余数据:第21条及以后按指定排序规则返回
- 关键词搜索时:不触发随机,正常返回搜索结果
查询参数
| 参数 | 类型 | 必填 | 说明 |
| keyword | String | 否 | 搜索关键词(电影标题模糊匹配) |
| sortBy | String | 否 | 排序字段:createTime(创建时间)/ updateTime(更新时间)/ rating(评分)/ title(标题) |
| sortOrder | String | 否 | 排序方向:asc(升序)/ desc(降序),默认desc |
| pageNo | int | 否 | 页码,默认 1 |
| pageSize | int | 否 | 每页条数,默认 50,最大 100 |
响应字段说明
| 字段 | 类型 | 说明 |
| total | Long | 电影总数 |
| pages | Long | 总页数 |
| pageNo | Long | 当前页码 |
| pageSize | Long | 每页条数 |
| list[].id | Long | 电影ID |
| list[].driveId | Long | 所属网盘ID |
| list[].folderId | Long | 所属文件夹ID |
| list[].title | String | 电影名称 |
| list[].originalName | String | 原始文件名 |
| list[].mimeType | String | MIME类型 |
| list[].fileSize | Long | 文件大小(字节) |
| list[].extension | String | 文件扩展名 |
| list[].description | String | 描述 |
| list[].scrapeStatus | String | 刮削状态:pending/scraping/success/failed |
| list[].rating | Integer | 评分(0-100) |
| list[].tags | String | 标签 |
| list[].casts | String | 主演(逗号分隔,TMDB刮削) |
| list[].directors | String | 导演(逗号分隔,TMDB刮削) |
| list[].posterUrl | String | 竖版海报URL(2:3 比例) |
| list[].backdropUrl | String | 横版背景图URL(16:9 比例) |
| list[].minioUrl | String | MinIO 文件访问URL |
| list[].createTime | String | 创建时间 |
| list[].updateTime | String | 更新时间 |
成功响应示例
{
"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);
});
功能说明
查询单部电影的完整详情信息,包括基本信息、海报、评分、简介、导演、主演等。
响应字段说明
| 字段 | 类型 | 说明 |
| id | Long | 电影ID |
| driveId | Long | 所属网盘ID |
| folderId | Long | 所属文件夹ID |
| title | String | 电影名称 |
| originalName | String | 原始文件名 |
| mimeType | String | MIME类型 |
| fileSize | Long | 文件大小(字节) |
| extension | String | 文件扩展名 |
| description | String | 电影描述 |
| scrapeStatus | String | 刮削状态:pending/scraping/success/failed |
| rating | Integer | 评分(0-100) |
| casts | String | 主演(逗号分隔,TMDB刮削) |
| directors | String | 导演(逗号分隔,TMDB刮削) |
| posterUrl | String | 竖版海报URL(2:3 比例) |
| backdropUrl | String | 横版背景图URL(16:9 比例) |
| tags | String | 标签(逗号分隔) |
| createTime | String | 创建时间 |
| updateTime | String | 更新时间 |
成功响应示例
{
"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
}
功能说明
查询单部电影的刮削状态,返回三种状态:刮削中(scraping)、刮削失败(failed)、刮削成功(success)。
响应字段说明
| 字段 | 类型 | 说明 |
| movieId | Long | 电影ID |
| title | String | 电影名称 |
| status | String | 刮削状态:scraping / failed / success / unknown |
| message | String | 状态描述信息 |
| error | String | 失败原因(仅 status=failed 时返回) |
| posterUrl | String | 竖版海报URL(仅 status=success 时返回) |
| backdropUrl | String | 横版背景图URL(仅 status=success 时返回) |
| rating | Integer | 评分(仅 status=success 时返回) |
| category | String | 分类(仅 status=success 时返回) |
| tags | String | 标签(仅 status=success 时返回) |
| casts | String | 主演(仅 status=success 时返回) |
| directors | String | 导演(仅 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
}
功能说明
创建新的电影记录,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
}
请求体(部分更新,所有字段可选)
{
"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
}
成功响应示例
{
"code": 200,
"message": "删除成功",
"data": null
}
失败响应示例 — 电影不存在
{
"code": 404,
"message": "电影不存在",
"data": null
}
失败响应示例 — 未登录或 Token 无效
{
"code": 401,
"message": "未登录或 Token 已过期",
"data": null
}
成功响应示例
{
"code": 200,
"message": "批量删除成功",
"data": {
"deletedCount": 3
}
}
失败响应示例 — 参数错误
{
"code": 400,
"message": "电影ID列表不能为空",
"data": null
}
失败响应示例 — 未登录或 Token 无效
{
"code": 401,
"message": "未登录或 Token 已过期",
"data": null
}
功能说明
获取当前用户的电影数量统计,按刮削类型和分类分组。
响应示例
{
"code": 200,
"data": {
"totalCount": 10,
"scrapedCount": 8,
"pendingCount": 2
}
}
失败响应示例 — 未登录或 Token 无效
{
"code": 401,
"message": "未登录或 Token 已过期",
"data": null
}
功能说明
获取当前用户配置的 TMDB API Key。
成功响应示例
{
"code": 200,
"message": "查询成功",
"data": {
"tmdbApiKey": "your_api_key_here"
}
}
失败响应示例 — 未登录或 Token 无效
{
"code": 401,
"message": "未登录或 Token 已过期",
"data": null
}
请求体
{ "tmdbApiKey": "你的API Key" }
功能说明
手动触发单部电影刮削,自动匹配TMDB最佳结果。刮削完成后会更新电影的简介、导演、主演、海报等信息。注意:两次请求需间隔5秒,防止频繁点击。
请求方式
支持两种方式传递 movieId:
1. URL 查询参数:?movieId=1
2. 请求体 JSON:
{ "movieId": 1 }
响应字段说明
| 字段 | 类型 | 说明 |
| movieId | Long | 电影ID |
| userId | Long | 用户ID |
| tmdbId | Long | TMDB电影ID |
| title | String | 电影标题(来自TMDB) |
| originalTitle | String | 原始标题(外文) |
| releaseDate | String | 上映日期 |
| posterPath | String | TMDB海报路径 |
| backdropPath | String | TMDB背景图路径 |
| localPosterUrl | String | 本地竖版海报URL(MinIO) |
| localBackdropUrl | String | 本地横版背景图URL(MinIO) |
| tmdbRating | Integer | TMDB评分(0-100分制) |
| voteCount | Integer | 投票数 |
| runtime | Integer | 片长(分钟) |
| genres | String | 类型(逗号分隔) |
| directors | String | 导演(逗号分隔,最多2位) |
| casts | String | 主演(逗号分隔,最多5位) |
| originalLanguage | String | 原始语言 |
| productionCountries | String | 制片国家(逗号分隔) |
| cacheTime | String | 缓存时间 |
| status | String | 缓存状态 |
成功响应示例
{
"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
}
功能说明
根据电影ID自动获取标题,搜索TMDB返回所有匹配结果。如果查询出多条结果,前端应展示列表让用户选择。
查询参数
| 参数 | 类型 | 必填 | 说明 |
| movieId | Long | 是 | 电影ID(自动获取标题搜索) |
| keyword | String | 否 | 自定义搜索关键词(不传则使用电影标题) |
响应字段说明
| 字段 | 类型 | 说明 |
| movieId | Long | 电影ID |
| keyword | String | 搜索关键词 |
| total | Integer | 搜索结果总数 |
| list[].tmdbId | Long | TMDB电影ID(用于确认刮削) |
| list[].title | String | 电影标题 |
| list[].originalTitle | String | 原始标题 |
| list[].releaseDate | String | 上映日期 |
| list[].posterPath | String | 海报图片URL |
| list[].voteAverage | Double | 评分 |
响应示例
{
"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
}
]
}
}
功能说明
用户从搜索结果中选择一部电影后,调用此接口执行刮削。刮削完成后会更新电影的简介、导演、主演、海报等信息。注意:两次请求需间隔5秒,防止频繁点击。
请求体
{
"movieId": 23,
"tmdbId": 597
}
响应字段说明
| 字段 | 类型 | 说明 |
| movieId | Long | 电影ID |
| userId | Long | 用户ID |
| tmdbId | Long | TMDB电影ID |
| title | String | 电影标题(来自TMDB) |
| originalTitle | String | 原始标题(外文) |
| releaseDate | String | 上映日期 |
| posterPath | String | TMDB海报路径 |
| backdropPath | String | TMDB背景图路径 |
| localPosterUrl | String | 本地竖版海报URL(MinIO) |
| localBackdropUrl | String | 本地横版背景图URL(MinIO) |
| tmdbRating | Integer | TMDB评分(0-100分制) |
| voteCount | Integer | 投票数 |
| runtime | Integer | 片长(分钟) |
| genres | String | 类型(逗号分隔) |
| directors | String | 导演(逗号分隔,最多2位) |
| casts | String | 主演(逗号分隔,最多5位) |
| originalLanguage | String | 原始语言 |
| productionCountries | String | 制片国家(逗号分隔) |
| cacheTime | String | 缓存时间 |
| status | String | 缓存状态 |
成功响应示例
{
"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
}
请求体(movieIds 可选)
{ "movieIds": [1, 2, 3] } // 可选:不传则自动查找需要刮削的电影
响应示例
{
"code": 200,
"message": "批量刮削完成",
"data": {
"successCount": 2,
"failCount": 0,
"total": 2
}
}
查询参数
| 参数 | 默认值 | 说明 |
| pageNo | 1 | 页码 |
| pageSize | 20 | 每页条数 |
响应示例
{
"code": 200,
"data": {
"totalCaches": 5,
"activeCaches": 3,
"expiredCaches": 2,
"userId": 2
}
}
功能说明
手动触发 TMDB 定时扫描任务,查找需要刮削的电影并执行刮削。无需请求体。
响应示例
{
"code": 200,
"message": "扫描任务已触发",
"data": {
"message": "扫描任务已触发",
"triggeredBy": 2,
"triggerType": "manual"
}
}
功能说明
分页查询定时任务执行记录,返回每条记录的汇总信息和详细结果(包含每部电影的刮削结果和失败原因)。
查询参数
| 参数 | 类型 | 必填 | 说明 |
| pageNo | Integer | 否 | 页码,默认 1 |
| pageSize | Integer | 否 | 每页条数,默认 20,最大 50 |
响应字段说明
| 字段 | 类型 | 说明 |
| total | Long | 总记录数 |
| pageNo | Integer | 当前页码 |
| pageSize | Integer | 每页条数 |
| list[].id | Long | 日志ID |
| list[].trigger_type | String | 触发方式:auto(定时自动)/ manual(手动触发) |
| list[].status | String | 执行状态:success / partial / failed / skipped |
| list[].total_processed | Integer | 总处理电影数 |
| list[].total_success | Integer | 成功刮削数 |
| list[].total_skipped | Integer | 跳过数 |
| list[].total_failed | Integer | 失败数 |
| list[].user_count | Integer | 涉及用户数 |
| list[].duration_ms | Long | 执行耗时(毫秒) |
| list[].start_time | String | 开始时间 |
| list[].end_time | String | 结束时间 |
| list[].error_message | String | 错误信息(如有) |
| list[].details | Array | 详细执行结果(解析后的JSON) |
| list[].details[].username | String | 用户名 |
| list[].details[].userId | Long | 用户ID |
| list[].details[].processed | Integer | 该用户处理的电影数 |
| list[].details[].success | Integer | 该用户成功刮削数 |
| list[].details[].skipped | Integer | 该用户跳过数 |
| list[].details[].failed | Integer | 该用户失败数 |
| list[].details[].messages | Array | 失败详情列表(格式:电影名称: 失败原因) |
响应示例
{
"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未找到匹配的电影"
]
}
]
}
]
}
}
功能说明
查询单条刮削日志的详细信息,包括每个用户的刮削结果、失败电影列表及原因。
响应字段说明
| 字段 | 类型 | 说明 |
| id | Long | 日志ID |
| trigger_type | String | 触发方式:auto(定时自动)/ manual(手动触发) |
| status | String | 执行状态:success / partial / failed / skipped |
| total_processed | Integer | 总处理电影数 |
| total_success | Integer | 成功刮削数 |
| total_skipped | Integer | 跳过数 |
| total_failed | Integer | 失败数 |
| user_count | Integer | 涉及用户数 |
| duration_ms | Long | 执行耗时(毫秒) |
| start_time | String | 开始时间 |
| end_time | String | 结束时间 |
| error_message | String | 错误信息(如有) |
| details | Array | 详细执行结果(解析后的JSON) |
| details[].username | String | 用户名 |
| details[].userId | Long | 用户ID |
| details[].processed | Integer | 该用户处理的电影数 |
| details[].success | Integer | 该用户成功刮削数 |
| details[].skipped | Integer | 该用户跳过数 |
| details[].failed | Integer | 该用户失败数 |
| details[].messages | Array | 失败详情列表(格式:电影名称: 失败原因) |
响应示例
{
"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
}
功能说明
删除指定电影的 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网盘
前端
→
Gateway
→
Drive
→
用户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 连接隔离)
请求头
Authorization: Bearer <token>
Content-Type: application/json
请求体
{
"openlistUrl": "http://www.example.com:3000",
"openlistUsername": "用户名",
"openlistPassword": "密码"
}
字段说明
| 字段 | 类型 | 必填 | 说明 |
| openlistUrl | String | 是 | OpenList 服务地址(如 http://example.com:13000) |
| openlistUsername | String | 是 | OpenList 账号 |
| openlistPassword | String | 是 | OpenList 密码 |
成功响应示例
{
"code": 200,
"message": "连接成功",
"data": null
}
失败响应示例
{
"code": 500,
"message": "连接失败:Invalid username or password",
"data": null
}
请求头
Authorization: Bearer <token>
Content-Type: application/json
请求体
{
"openlistUrl": "http://example.com:13000",
"openlistUsername": "your_username",
"openlistPassword": "your_password"
}
字段说明
| 字段 | 类型 | 必填 | 说明 |
| openlistUrl | String | 是 | OpenList 服务地址 |
| openlistUsername | String | 是 | OpenList 账号 |
| openlistPassword | String | 是 | OpenList 密码 |
功能说明
验证连接 → 保存配置(敏感信息 Jasypt 加密) → 缓存 Token(24小时有效期)。
每个用户只能绑定一个 OpenList 实例,重复绑定会更新配置。
绑定成功后自动触发异步同步:递归遍历 OpenList 目录树,同步文件夹层级和视频文件到数据库。
同步只处理视频格式文件(mp4, mkv, avi 等),非视频文件自动跳过。
成功响应示例
{
"code": 200,
"message": "绑定成功,正在后台同步文件...",
"data": null
}
失败响应示例
{
"code": 500,
"message": "绑定失败:连接失败:Invalid username or password",
"data": null
}
请求头
Authorization: Bearer <token>
功能说明
删除当前用户的 OpenList 连接配置(包括加密的 URL、账号、密码和 Token 缓存)。
解绑后同步文件和播放链接接口将不可用。
成功响应示例
{
"code": 200,
"message": "解绑成功",
"data": null
}
失败响应示例
{
"code": 500,
"message": "解绑失败:未找到绑定记录",
"data": null
}
请求头
Authorization: Bearer <token>
功能说明
返回脱敏后的连接信息(URL、用户名均脱敏显示,不返回明文密码)。
会实际调用 OpenList 登录接口验证当前连接是否可用,返回 connected 和 connectMessage 字段。
未绑定时返回 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"
}
}
响应字段说明
| 字段 | 说明 |
| openlistUrl | OpenList 服务地址(脱敏显示,如 http://ld***:13000) |
| openlistUsername | OpenList 账号(脱敏显示,如 dj***22) |
| storageId | OpenList 存储 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
}
请求头
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": []
}
}
响应字段说明
| 字段 | 类型 | 说明 |
| driveCount | int | 关联网盘数量(通常为 1) |
| folderCount | int | 本次同步创建的文件夹数量 |
| movieCount | int | 本次同步创建的视频文件(电影)数量 |
| skippedFileCount | int | 跳过的非视频文件数量 |
| skippedFolderCount | int | 跳过的不含视频的文件夹数量 |
| errorCount | int | 同步过程中的错误数量 |
| errors | Array | 错误详情列表(字符串数组) |
失败响应示例
{
"code": 500,
"message": "同步失败:用户未绑定 OpenList,请先绑定",
"data": null
}
请求头
Authorization: Bearer <token>
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘 ID |
| folderId | Long | 文件夹 ID(0 表示根目录) |
功能说明
使用当前用户绑定的 OpenList 连接,获取指定路径下的文件列表,自动创建数据库中缺失的电影记录。
已存在的文件不会重复创建。同步后文件可进行 TMDB 刮削。
前置条件:用户已通过 /api/openlist/bind 绑定 OpenList 连接。
成功响应示例
{
"code": 200,
"message": "同步成功",
"data": {
"syncCount": 5,
"totalFiles": 10
}
}
响应字段说明
| 字段 | 说明 |
| syncCount | 本次新增的电影记录数量 |
| totalFiles | OpenList 中该路径下的文件总数 |
失败响应示例
{
"code": 500,
"message": "同步失败:用户未绑定 OpenList,请先在设置中绑定 OpenList 连接",
"data": null
}
请求头
Authorization: Bearer <token>
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘 ID |
| movieId | Long | 电影 ID |
功能说明
使用当前用户绑定的 OpenList 连接,获取文件的真实下载地址,返回的 URL 可直接用于视频播放器播放。
仅支持 storageType 为 openlist 的电影记录。
Token 过期时自动刷新,对用户透明。
成功响应示例
{
"code": 200,
"message": "获取成功",
"data": {
"playUrl": "https://uc网盘真实下载地址/xxx.mp4",
"fileName": "阿凡达.mp4",
"fileSize": 1073741824
}
}
响应字段说明
| 字段 | 说明 |
| playUrl | 真实播放/下载地址,可直接用于视频播放器 |
| fileName | 文件名 |
| fileSize | 文件大小(字节) |
失败响应示例
{
"code": 500,
"message": "获取失败:该电影不是 OpenList 管理的文件",
"data": null
}
请求头
Authorization: Bearer <token>
路径参数
| 参数 | 类型 | 说明 |
| driveId | Long | 网盘 ID |
| folderId | Long | 文件夹 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 相关字段说明
| 字段 | 说明 |
| openlistFilePath | OpenList 文件路径(如 "/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>