1. 新增用户OpenList连接配置模块,包括数据库表、实体类与相关服务 2. 为驱动服务添加定时心跳检测功能,监控OpenList连接状态 3. 重构电影实体与数据库字段,移除冗余的overview字段,统一使用description存储简介 4. 完善TMDB刮削逻辑,增加空指针校验与详细失败日志 5. 配置文件敏感信息加密,添加加密脚本工具 6. 新增网关路由规则支持OpenList接口 7. 完善刮削日志查询功能,支持查看详细执行结果
4683 lines
260 KiB
HTML
4683 lines
260 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>网盘系统 API 接口文档</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
padding: 20px;
|
||
}
|
||
.container {
|
||
background: white;
|
||
border-radius: 16px;
|
||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||
max-width: 1000px;
|
||
margin: 0 auto;
|
||
overflow: hidden;
|
||
}
|
||
.header {
|
||
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||
color: white;
|
||
padding: 30px 40px;
|
||
text-align: center;
|
||
}
|
||
.header h1 { font-size: 28px; margin-bottom: 8px; }
|
||
.header p { color: #a0aec0; font-size: 14px; }
|
||
.gateway-badge {
|
||
display: inline-block;
|
||
background: #48bb78;
|
||
color: white;
|
||
padding: 6px 16px;
|
||
border-radius: 20px;
|
||
font-size: 13px;
|
||
margin-top: 12px;
|
||
font-family: monospace;
|
||
}
|
||
.nav {
|
||
display: flex;
|
||
background: #f7fafc;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
overflow-x: auto;
|
||
}
|
||
.nav-item {
|
||
padding: 14px 24px;
|
||
cursor: pointer;
|
||
border-bottom: 3px solid transparent;
|
||
white-space: nowrap;
|
||
font-size: 14px;
|
||
color: #4a5568;
|
||
transition: all 0.2s;
|
||
}
|
||
.nav-item:hover { background: #edf2f7; }
|
||
.nav-item.active {
|
||
border-bottom-color: #667eea;
|
||
color: #667eea;
|
||
font-weight: 600;
|
||
}
|
||
.badge-danger { background: #e53e3e; color: white; padding: 2px 8px; border-radius: 4px; font-size: 12px; }
|
||
.content { padding: 24px 40px; }
|
||
.section { display: none; }
|
||
.section.active { display: block; }
|
||
.service-card {
|
||
background: #f7fafc;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin-bottom: 20px;
|
||
border-left: 4px solid #667eea;
|
||
}
|
||
.service-card h2 {
|
||
font-size: 18px;
|
||
color: #2d3748;
|
||
margin-bottom: 4px;
|
||
}
|
||
.service-card .port {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
font-family: monospace;
|
||
}
|
||
.api-item {
|
||
background: white;
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 8px;
|
||
margin: 12px 0;
|
||
overflow: hidden;
|
||
}
|
||
.api-header {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 14px 18px;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
.api-header:hover { background: #f7fafc; }
|
||
.method {
|
||
display: inline-block;
|
||
padding: 4px 10px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
font-family: monospace;
|
||
margin-right: 12px;
|
||
min-width: 65px;
|
||
text-align: center;
|
||
}
|
||
.method.GET { background: #c6f6d5; color: #276749; }
|
||
.method.POST { background: #bee3f8; color: #2a4365; }
|
||
.method.PUT { background: #fefcbf; color: #744210; }
|
||
.method.DELETE { background: #fed7d7; color: #742a2a; }
|
||
.api-path {
|
||
font-family: monospace;
|
||
font-size: 14px;
|
||
color: #2d3748;
|
||
flex: 1;
|
||
}
|
||
.api-desc {
|
||
font-size: 13px;
|
||
color: #718096;
|
||
margin-left: 12px;
|
||
}
|
||
.api-detail {
|
||
display: none;
|
||
padding: 0 18px 18px;
|
||
border-top: 1px solid #e2e8f0;
|
||
}
|
||
.api-detail.show { display: block; }
|
||
.detail-section { margin: 12px 0; }
|
||
.detail-section h4 {
|
||
font-size: 13px;
|
||
color: #4a5568;
|
||
margin-bottom: 6px;
|
||
font-weight: 600;
|
||
}
|
||
.param-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 13px;
|
||
}
|
||
.param-table th, .param-table td {
|
||
padding: 8px 12px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
.param-table th {
|
||
background: #f7fafc;
|
||
color: #4a5568;
|
||
font-weight: 600;
|
||
}
|
||
.param-table td { color: #2d3748; }
|
||
.code-block {
|
||
background: #1a202c;
|
||
color: #a0aec0;
|
||
padding: 14px 18px;
|
||
border-radius: 6px;
|
||
font-family: 'Fira Code', monospace;
|
||
font-size: 12px;
|
||
overflow-x: auto;
|
||
line-height: 1.6;
|
||
}
|
||
.code-block .key { color: #63b3ed; }
|
||
.code-block .string { color: #68d391; }
|
||
.code-block .number { color: #f6ad55; }
|
||
.code-block .keyword { color: #fc8181; }
|
||
.status-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-top: 12px;
|
||
}
|
||
.status-table th, .status-table td {
|
||
padding: 10px 14px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
font-size: 13px;
|
||
}
|
||
.status-table th { background: #f7fafc; color: #4a5568; }
|
||
.arch-diagram {
|
||
background: #f7fafc;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
text-align: center;
|
||
margin: 16px 0;
|
||
}
|
||
.arch-flow {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
font-size: 13px;
|
||
}
|
||
.arch-box {
|
||
padding: 8px 16px;
|
||
border-radius: 6px;
|
||
color: white;
|
||
font-weight: 600;
|
||
}
|
||
.arch-box.gateway { background: #48bb78; }
|
||
.arch-box.user { background: #4299e1; }
|
||
.arch-box.drive { background: #ed8936; }
|
||
.arch-box.tmdb { background: #9f7aea; }
|
||
.arch-arrow { color: #a0aec0; font-size: 18px; }
|
||
|
||
/* 认证标识 */
|
||
.auth-badge {
|
||
display: inline-block;
|
||
background: #ed8936;
|
||
color: white;
|
||
padding: 2px 8px;
|
||
border-radius: 10px;
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
margin-left: 8px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
/* 搜索框样式 */
|
||
.search-container {
|
||
padding: 16px 40px;
|
||
background: #f7fafc;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
}
|
||
.search-box {
|
||
display: flex;
|
||
align-items: center;
|
||
background: white;
|
||
border: 2px solid #e2e8f0;
|
||
border-radius: 8px;
|
||
padding: 10px 16px;
|
||
transition: all 0.2s;
|
||
}
|
||
.search-box:focus-within {
|
||
border-color: #667eea;
|
||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||
}
|
||
.search-icon {
|
||
color: #a0aec0;
|
||
margin-right: 10px;
|
||
font-size: 16px;
|
||
}
|
||
.search-input {
|
||
flex: 1;
|
||
border: none;
|
||
outline: none;
|
||
font-size: 14px;
|
||
color: #2d3748;
|
||
}
|
||
.search-input::placeholder {
|
||
color: #a0aec0;
|
||
}
|
||
.search-clear {
|
||
cursor: pointer;
|
||
color: #a0aec0;
|
||
font-size: 18px;
|
||
padding: 0 4px;
|
||
display: none;
|
||
}
|
||
.search-clear:hover {
|
||
color: #e53e3e;
|
||
}
|
||
.search-clear.show {
|
||
display: block;
|
||
}
|
||
.search-result-count {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
margin-top: 8px;
|
||
display: none;
|
||
}
|
||
.search-result-count.show {
|
||
display: block;
|
||
}
|
||
/* 搜索高亮 */
|
||
.highlight {
|
||
background: #fefcbf;
|
||
padding: 1px 2px;
|
||
border-radius: 2px;
|
||
}
|
||
/* 搜索时隐藏不匹配的项 */
|
||
.api-item.search-hidden {
|
||
display: none;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>网盘系统 API 接口文档</h1>
|
||
<p>微服务架构 · 统一网关入口</p>
|
||
<div class="gateway-badge">网关地址:http://localhost:8888</div>
|
||
</div>
|
||
|
||
<div class="nav">
|
||
<div class="nav-item active" data-section="overview">架构总览</div>
|
||
<div class="nav-item" data-section="user">用户服务</div>
|
||
<div class="nav-item" data-section="users">用户管理</div>
|
||
<div class="nav-item" data-section="profile">个人资料</div>
|
||
<div class="nav-item" data-section="drive">网盘服务</div>
|
||
<div class="nav-item" data-section="icon">Icon服务</div>
|
||
<div class="nav-item" data-section="tmdb">TMDB服务</div>
|
||
<div class="nav-item" data-section="openlist">OpenList集成</div>
|
||
<div class="nav-item" data-section="status">状态码</div>
|
||
</div>
|
||
|
||
<!-- 搜索框 -->
|
||
<div class="search-container">
|
||
<div class="search-box">
|
||
<span class="search-icon">🔍</span>
|
||
<input type="text" class="search-input" id="apiSearch" placeholder="搜索接口路径、描述或功能...">
|
||
<span class="search-clear" id="searchClear">×</span>
|
||
</div>
|
||
<div class="search-result-count" id="searchResultCount"></div>
|
||
</div>
|
||
|
||
<div class="content">
|
||
<!-- 架构总览 -->
|
||
<div class="section active" id="overview">
|
||
<div class="arch-diagram">
|
||
<h3 style="margin-bottom: 16px; color: #2d3748;">服务架构图</h3>
|
||
<div class="arch-flow">
|
||
<div class="arch-box" style="background: #4a5568;">前端</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box gateway">Gateway:8888</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box user">User:8081</div>
|
||
</div>
|
||
<div class="arch-flow" style="margin-top: 8px;">
|
||
<div class="arch-box" style="background: #4a5568; visibility: hidden;">前端</div>
|
||
<span class="arch-arrow" style="visibility: hidden;">→</span>
|
||
<div class="arch-box gateway" style="visibility: hidden;">Gateway</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box drive">Drive:8082</div>
|
||
</div>
|
||
<div class="arch-flow" style="margin-top: 8px;">
|
||
<div class="arch-box" style="background: #4a5568; visibility: hidden;">前端</div>
|
||
<span class="arch-arrow" style="visibility: hidden;">→</span>
|
||
<div class="arch-box gateway" style="visibility: hidden;">Gateway</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box tmdb">TMDB:8083</div>
|
||
</div>
|
||
<div class="arch-flow" style="margin-top: 8px;">
|
||
<div class="arch-box" style="background: #4a5568; visibility: hidden;">前端</div>
|
||
<span class="arch-arrow" style="visibility: hidden;">→</span>
|
||
<div class="arch-box gateway" style="visibility: hidden;">Gateway</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box drive">Drive:8082</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box" style="background: #38b2ac;">OpenList:13000</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box" style="background: #e53e3e;">各网盘</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="service-card">
|
||
<h2>路由规则</h2>
|
||
<table class="param-table">
|
||
<tr><th>请求路径</th><th>转发服务</th><th>说明</th></tr>
|
||
<tr><td>/api/login, /api/register</td><td>user-service</td><td>登录注册</td></tr>
|
||
<tr><td>/api/token/**, /api/login-logs/**</td><td>user-service</td><td>令牌/日志</td></tr>
|
||
<tr><td>/api/users/**</td><td>user-service</td><td>用户管理</td></tr>
|
||
<tr><td>/api/profile/**</td><td>user-service</td><td>个人资料</td></tr>
|
||
<tr><td>/api/drive/**</td><td>drive-service</td><td>网盘管理</td></tr>
|
||
<tr><td>/api/drive/**/sync-files</td><td>drive-service</td><td>OpenList 文件同步</td></tr>
|
||
<tr><td>/api/drive/**/play-url</td><td>drive-service</td><td>OpenList 播放链接</td></tr>
|
||
<tr><td>/api/icon/**</td><td>drive-service</td><td>Icon管理</td></tr>
|
||
<tr><td>/api/movies/**</td><td>tmdb-service</td><td>电影管理</td></tr>
|
||
<tr><td>/api/tmdb/**, /api/tmdb-scraper/**</td><td>tmdb-service</td><td>TMDB刮削</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户服务 -->
|
||
<div class="section" id="user">
|
||
<div class="service-card">
|
||
<h2>用户认证服务</h2>
|
||
<span class="port">wangpan-user-service : 8081</span>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/login</span>
|
||
<span class="api-desc">用户登录</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"username"</span>: <span class="string">"用户名"</span>,
|
||
<span class="key">"password"</span>: <span class="string">"密码"</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"登录成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">7</span>,
|
||
<span class="key">"username"</span>: <span class="string">"zhangsan"</span>,
|
||
<span class="key">"userType"</span>: <span class="number">0</span>, <span style="color:#718096">// 0=普通用户, 1=VIP, 2=管理员</span>
|
||
<span class="key">"status"</span>: <span class="number">1</span>,
|
||
<span class="key">"email"</span>: <span class="string">"xxx@xx.com"</span>,
|
||
<span class="key">"phone"</span>: <span class="string">"138****0000"</span>,
|
||
<span class="key">"realName"</span>: <span class="string">"张三"</span>,
|
||
<span class="key">"avatarUrl"</span>: <span class="string">""</span>,
|
||
<span class="key">"lastLoginIp"</span>: <span class="string">"127.0.0.1"</span>,
|
||
<span class="key">"lastLoginTime"</span>: <span class="string">"2026-06-10T11:20:21"</span>,
|
||
<span class="key">"hasTmdbKey"</span>: <span class="keyword">false</span>,
|
||
<span class="key">"token"</span>: <span class="string">"tk_xxx..."</span>,
|
||
<span class="key">"tokenType"</span>: <span class="string">"Bearer"</span>,
|
||
<span class="key">"expireIn"</span>: <span class="number">604800</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 用户名或密码错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名或密码错误"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 账号被禁用</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"账号已被禁用,请联系管理员"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数缺失</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名和密码不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>userType 说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>值</th><th>角色</th><th>权限说明</th></tr>
|
||
<tr><td><strong>0</strong></td><td>普通用户</td><td>仅访问自己的数据(如日志只看自己的)</td></tr>
|
||
<tr><td><strong>1</strong></td><td>VIP 用户</td><td>普通用户 + 更多存储空间</td></tr>
|
||
<tr><td><strong>2</strong></td><td>管理员</td><td>可查看所有用户数据(如全部日志)</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/register</span>
|
||
<span class="api-desc">用户注册</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"username"</span>: <span class="string">"用户名"</span>,
|
||
<span class="key">"password"</span>: <span class="string">"密码"</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"注册成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">10</span>,
|
||
<span class="key">"username"</span>: <span class="string">"newuser"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 用户名已存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">409</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名已被占用"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数缺失</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名和密码不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/token/validate</span>
|
||
<span class="api-desc">令牌验证</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例 — Token 有效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"Token 有效"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"valid"</span>: <span class="keyword">true</span>,
|
||
<span class="key">"userId"</span>: <span class="number">7</span>,
|
||
<span class="key">"username"</span>: <span class="string">"zhangsan"</span>,
|
||
<span class="key">"expireTime"</span>: <span class="string">"2026-06-17T11:20:21"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — Token 无效或已过期</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"Token 无效或已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/login-logs</span>
|
||
<span class="api-desc">登录日志查询</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>权限说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>用户类型</th><th>可见范围</th></tr>
|
||
<tr><td>普通用户(userType=0/1)</td><td style="color:#e53e3e">仅自己的登录记录</td></tr>
|
||
<tr><td>管理员(userType=2)</td><td style="color:#38a169">所有用户的登录记录</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>username</td><td>String</td><td>否</td><td>按用户名模糊筛选(管理员可用)</td></tr>
|
||
<tr><td>result</td><td>String</td><td>否</td><td>登录结果:success / fail</td></tr>
|
||
<tr><td>startDate</td><td>String</td><td>否</td><td>开始日期(含),格式:yyyy-MM-dd,如 2026-06-01</td></tr>
|
||
<tr><td>endDate</td><td>String</td><td>否</td><td>结束日期(含),格式:yyyy-MM-dd,如 2026-06-10</td></tr>
|
||
<tr><td>pageNo</td><td>int</td><td>否</td><td>页码,默认 1</td></tr>
|
||
<tr><td>pageSize</td><td>int</td><td>否</td><td>每页条数,默认 20</td></tr>
|
||
</table>
|
||
<p style="color:#718096; font-size:13px; margin-top:8px;">提示:可组合使用多个筛选条件,如 ?result=fail&startDate=2026-06-01&endDate=2026-06-10</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"total"</span>: <span class="number">10</span>,
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"userId"</span>: <span class="number">7</span>,
|
||
<span class="key">"username"</span>: <span class="string">"zhangsan"</span>,
|
||
<span class="key">"loginIp"</span>: <span class="string">"127.0.0.1"</span>,
|
||
<span class="key">"loginTime"</span>: <span class="string">"2026-06-10T11:20:21"</span>,
|
||
<span class="key">"result"</span>: <span class="string">"success"</span>,
|
||
<span class="key">"userAgent"</span>: <span class="string">"Mozilla/5.0..."</span>
|
||
}
|
||
],
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">20</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"权限不足,无法查看其他用户的登录日志"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户管理(管理员专用) -->
|
||
<div class="section" id="users">
|
||
<div class="service-card">
|
||
<h2>用户管理</h2>
|
||
<span class="port">wangpan-user-service : 8081</span>
|
||
<span class="badge badge-danger">仅管理员 (userType=2)</span>
|
||
</div>
|
||
|
||
<!-- 查询用户列表 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/users</span>
|
||
<span class="api-desc">查询用户列表</span><span class="auth-badge">需Token + 管理员</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>权限说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>角色</th><th>访问权限</th></tr>
|
||
<tr><td>普通/VIP 用户</td><td style="color:#e53e3e">403 权限不足</td></tr>
|
||
<tr><td><strong>管理员</strong></td><td style="color:#38a169">可查看所有用户列表</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>username</td><td>String</td><td>否</td><td>用户名模糊搜索</td></tr>
|
||
<tr><td>status</td><td>int</td><td>否</td><td>状态:0=禁用, 1=正常, 2=锁定</td></tr>
|
||
<tr><td>userType</td><td>int</td><td>否</td><td>角色:0=普通, 1=VIP, 2=管理员</td></tr>
|
||
<tr><td>pageNo</td><td>int</td><td>否</td><td>页码,默认 1</td></tr>
|
||
<tr><td>pageSize</td><td>int</td><td>否</td><td>每页条数,默认 20</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"total"</span>: <span class="number">5</span>,
|
||
<span class="key">"list"</span>: [
|
||
{ <span class="key">"id"</span>: <span class="number">7</span>, <span class="key">"username"</span>: <span class="string">"zhangsan"</span>, <span class="key">"userType"</span>: <span class="number">0</span>, <span class="key">"status"</span>: <span class="number">1</span>, ... }
|
||
],
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">20</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"权限不足,仅管理员可访问"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 新增用户 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/users</span>
|
||
<span class="api-desc">新增用户</span><span class="auth-badge">需Token + 管理员</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"username"</span>: <span class="string">"必填"</span>,
|
||
<span class="key">"password"</span>: <span class="string">"必填"</span>,
|
||
<span class="key">"email"</span>: <span class="string">"可选"</span>,
|
||
<span class="key">"phone"</span>: <span class="string">"可选"</span>,
|
||
<span class="key">"realName"</span>: <span class="string">"可选"</span>,
|
||
<span class="key">"status"</span>: <span class="number">1</span>, <span style="color:#718096">// 可选:0=禁用, 1=正常, 默认1</span>
|
||
<span class="key">"userType"</span>: <span class="number">0</span> <span style="color:#718096">// 可选:0=普通, 1=VIP, 2=管理员, 默认0</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户创建成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">10</span>,
|
||
<span class="key">"username"</span>: <span class="string">"newuser"</span>,
|
||
<span class="key">"email"</span>: <span class="string">"new@test.com"</span>,
|
||
<span class="key">"status"</span>: <span class="number">1</span>,
|
||
<span class="key">"userType"</span>: <span class="number">0</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 用户名已存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">409</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名已被占用"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数缺失</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名或密码不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"非管理员,无权限操作"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 服务器错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"服务器内部错误"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 修改用户 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/users/{userId}</span>
|
||
<span class="api-desc">修改用户信息</span><span class="auth-badge">需Token + 管理员</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>userId</td><td>Long</td><td>目标用户 ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体(部分更新,只传需要修改的字段)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"email"</span>: <span class="string">"新邮箱"</span>, <span style="color:#718096">// 可选</span>
|
||
<span class="key">"phone"</span>: <span class="string">"新手机号"</span>, <span style="color:#718096">// 可选</span>
|
||
<span class="key">"realName"</span>: <span class="string">"新姓名"</span>, <span style="color:#718096">// 可选</span>
|
||
<span class="key">"status"</span>: <span class="number">0</span>, <span style="color:#718096">// 可选:0=禁用, 1=正常, 2=锁定</span>
|
||
<span class="key">"userType"</span>: <span class="number">1</span> <span style="color:#718096">// 可选:0=普通, 1=VIP, 2=管理员</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户信息更新成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">10</span>,
|
||
<span class="key">"username"</span>: <span class="string">"newuser"</span>,
|
||
<span class="key">"email"</span>: <span class="string">"new@test.com"</span>,
|
||
<span class="key">"status"</span>: <span class="number">1</span>,
|
||
<span class="key">"userType"</span>: <span class="number">0</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 用户不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"目标用户不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数缺失</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"没有需要更新的字段"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"非管理员,无权限操作"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 服务器错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"服务器内部错误"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 删除用户 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/users/{userId}</span>
|
||
<span class="api-desc">删除用户(软删除)</span><span class="auth-badge">需Token + 管理员</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>userId</td><td>Long</td><td>要删除的用户 ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>说明</h4>
|
||
<p>软删除操作,将 <code>is_deleted</code> 设为 1,不物理删除数据。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户删除成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 用户不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"目标用户不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 删除失败</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"删除失败,用户可能已被删除"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"非管理员,无权限操作"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 服务器错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"服务器内部错误"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 个人资料 -->
|
||
<div class="section" id="profile">
|
||
<div class="service-card">
|
||
<h2>个人资料</h2>
|
||
<span class="port">wangpan-user-service : 8081</span>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/profile</span>
|
||
<span class="api-desc">获取当前用户个人资料</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>获取当前登录用户的基本信息(不含密码等敏感字段)。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"username"</span>: <span class="string">"admin"</span>,
|
||
<span class="key">"email"</span>: <span class="string">"admin@example.com"</span>,
|
||
<span class="key">"phone"</span>: <span class="string">"13800138000"</span>,
|
||
<span class="key">"realName"</span>: <span class="string">"管理员"</span>,
|
||
<span class="key">"avatarUrl"</span>: <span class="string">"http://..."</span>,
|
||
<span class="key">"userType"</span>: <span class="number">2</span>,
|
||
<span class="key">"status"</span>: <span class="number">1</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2024-01-01T00:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 用户不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/profile/username</span>
|
||
<span class="api-desc">修改用户名</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>修改当前登录用户的用户名。</p>
|
||
<p><strong>校验规则:</strong></p>
|
||
<ul>
|
||
<li>用户名不能为空</li>
|
||
<li>新用户名不能与当前用户名相同</li>
|
||
<li>新用户名不能被其他用户占用</li>
|
||
</ul>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"username"</span>: <span class="string">"新用户名"</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名修改成功"</span>,
|
||
<span class="key">"data"</span>: null
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 用户名已存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">409</span>,
|
||
<span class="key">"message"</span>: <span class="string">"用户名已被其他用户使用"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"新用户名不能为空或与当前用户名相同"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/profile/avatar</span>
|
||
<span class="api-desc">修改头像</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>修改当前登录用户的头像。</p>
|
||
<p><strong>流程:</strong></p>
|
||
<ol>
|
||
<li>接收 Base64 编码的图片数据</li>
|
||
<li>解析 MIME 类型和 Base64 内容</li>
|
||
<li>上传到 MinIO(路径:avatar/{userId}_{timestamp}.{ext})</li>
|
||
<li>更新数据库中的 avatar_url 字段</li>
|
||
</ol>
|
||
<p><strong>支持的图片格式:</strong>PNG、JPG/JPEG、GIF、WebP</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"avatarData"</span>: <span class="string">"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"头像更新成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"avatarUrl"</span>: <span class="string">"http://your-server:39000/wangpan/avatar/1_1719234567890.png?..."</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 图片格式不支持</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"不支持的图片格式,仅支持 PNG、JPG、GIF、WebP"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — MinIO 上传失败</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"头像上传失败,请稍后重试"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 网盘服务 -->
|
||
<div class="section" id="drive">
|
||
<div class="service-card">
|
||
<h2>网盘管理服务</h2>
|
||
<span class="port">wangpan-drive-service : 8082</span>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/drive</span>
|
||
<span class="api-desc">创建网盘</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"name"</span>: <span class="string">"网盘名称"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"网盘描述"</span>,
|
||
<span class="key">"logoData"</span>: <span class="string">"data:image/png;base64,iVBOR..."</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>参数说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>name</td><td>String</td><td>是</td><td>网盘名称</td></tr>
|
||
<tr><td>description</td><td>String</td><td>否</td><td>网盘描述</td></tr>
|
||
<tr><td>logoData</td><td>String</td><td>否</td><td>Logo图片Base64编码,支持 data:image/xxx;base64, 前缀格式,上传后存储到MinIO,数据库仅保存URL</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"创建成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"我的网盘"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"个人网盘"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/drive/1/logo.png"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数缺失</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘名称不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive</span>
|
||
<span class="api-desc">查询网盘列表</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||
<tr><td>pageNo</td><td>1</td><td>页码</td></tr>
|
||
<tr><td>pageSize</td><td>20</td><td>每页条数</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>name</td><td>String</td><td>网盘名称</td></tr>
|
||
<tr><td>description</td><td>String</td><td>网盘描述</td></tr>
|
||
<tr><td>logoUrl</td><td>String</td><td>网盘Logo图片链接(MinIO存储地址)</td></tr>
|
||
<tr><td>folderCount</td><td>Integer</td><td>文件夹数量</td></tr>
|
||
<tr><td>fileCount</td><td>Integer</td><td>文件数量</td></tr>
|
||
<tr><td>totalSize</td><td>Long</td><td>总大小(字节)</td></tr>
|
||
<tr><td>status</td><td>String</td><td>状态</td></tr>
|
||
<tr><td>createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>updateTime</td><td>String</td><td>更新时间</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"total"</span>: <span class="number">2</span>,
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"我的网盘"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"个人网盘"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/drive/1/logo.png"</span>,
|
||
<span class="key">"folderCount"</span>: <span class="number">5</span>,
|
||
<span class="key">"fileCount"</span>: <span class="number">20</span>,
|
||
<span class="key">"totalSize"</span>: <span class="number">10737418240</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
],
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">20</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/{id}</span>
|
||
<span class="api-desc">查询网盘详情</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>网盘ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>name</td><td>String</td><td>网盘名称</td></tr>
|
||
<tr><td>description</td><td>String</td><td>网盘描述</td></tr>
|
||
<tr><td>logoUrl</td><td>String</td><td>网盘Logo图片链接(MinIO存储地址)</td></tr>
|
||
<tr><td>folderCount</td><td>Integer</td><td>文件夹数量</td></tr>
|
||
<tr><td>fileCount</td><td>Integer</td><td>文件数量</td></tr>
|
||
<tr><td>totalSize</td><td>Long</td><td>总大小(字节)</td></tr>
|
||
<tr><td>status</td><td>String</td><td>状态</td></tr>
|
||
<tr><td>createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>updateTime</td><td>String</td><td>更新时间</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"我的网盘"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"个人网盘"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/drive/1/logo.png"</span>,
|
||
<span class="key">"folderCount"</span>: <span class="number">5</span>,
|
||
<span class="key">"fileCount"</span>: <span class="number">20</span>,
|
||
<span class="key">"totalSize"</span>: <span class="number">10737418240</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 网盘不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/drive/{id}</span>
|
||
<span class="api-desc">更新网盘</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"name"</span>: <span class="string">"新网盘名称"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"新描述"</span>,
|
||
<span class="key">"logoData"</span>: <span class="string">"data:image/png;base64,iVBOR..."</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>参数说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>name</td><td>String</td><td>否</td><td>网盘名称</td></tr>
|
||
<tr><td>description</td><td>String</td><td>否</td><td>网盘描述</td></tr>
|
||
<tr><td>logoData</td><td>String</td><td>否</td><td>新的Logo图片Base64编码,传入后覆盖旧Logo;不传则保留原Logo</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"更新成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"新网盘名称"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"新描述"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/drive/1/logo.png"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T11:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 网盘不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"无权修改该网盘"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/drive/{id}</span>
|
||
<span class="api-desc">删除网盘</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>网盘ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"删除成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 网盘不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"无权删除该网盘"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders</span>
|
||
<span class="api-desc">创建文件夹</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"name"</span>: <span class="string">"文件夹名称"</span>,
|
||
<span class="key">"parentId"</span>: <span class="number">0</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"创建成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"parentId"</span>: <span class="number">0</span>,
|
||
<span class="key">"name"</span>: <span class="string">"我的电影"</span>,
|
||
<span class="key">"description"</span>: <span class="string">""</span>,
|
||
<span class="key">"depth"</span>: <span class="number">0</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数缺失</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"文件夹名称不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 网盘不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders</span>
|
||
<span class="api-desc">查询文件夹列表</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||
<tr><td>parentId</td><td>0</td><td>父文件夹 ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"parentId"</span>: <span class="number">0</span>,
|
||
<span class="key">"name"</span>: <span class="string">"我的电影"</span>,
|
||
<span class="key">"description"</span>: <span class="string">""</span>,
|
||
<span class="key">"depth"</span>: <span class="number">0</span>,
|
||
<span class="key">"childFolderCount"</span>: <span class="number">2</span>,
|
||
<span class="key">"fileCount"</span>: <span class="number">5</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
]
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 网盘不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders/{folderId}</span>
|
||
<span class="api-desc">查询文件夹详情</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>文件夹ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>文件夹ID</td></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>所属网盘ID</td></tr>
|
||
<tr><td>parentId</td><td>Long</td><td>父文件夹ID(0表示根目录)</td></tr>
|
||
<tr><td>name</td><td>String</td><td>文件夹名称</td></tr>
|
||
<tr><td>description</td><td>String</td><td>文件夹描述</td></tr>
|
||
<tr><td>depth</td><td>Integer</td><td>层级深度</td></tr>
|
||
<tr><td>childFolderCount</td><td>Integer</td><td>子文件夹数量</td></tr>
|
||
<tr><td>fileCount</td><td>Integer</td><td>文件数量</td></tr>
|
||
<tr><td>totalSize</td><td>Long</td><td>总大小(字节)</td></tr>
|
||
<tr><td>createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>updateTime</td><td>String</td><td>更新时间</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"parentId"</span>: <span class="number">0</span>,
|
||
<span class="key">"name"</span>: <span class="string">"我的电影"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"收藏的电影"</span>,
|
||
<span class="key">"depth"</span>: <span class="number">0</span>,
|
||
<span class="key">"childFolderCount"</span>: <span class="number">2</span>,
|
||
<span class="key">"fileCount"</span>: <span class="number">5</span>,
|
||
<span class="key">"totalSize"</span>: <span class="number">5368709120</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 文件夹不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"文件夹不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders/{folderId}</span>
|
||
<span class="api-desc">更新文件夹</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>文件夹ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体(部分更新)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"name"</span>: <span class="string">"新文件夹名称"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"新描述"</span>,
|
||
<span class="key">"parentId"</span>: <span class="number">0</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>参数说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>name</td><td>String</td><td>否</td><td>文件夹名称</td></tr>
|
||
<tr><td>description</td><td>String</td><td>否</td><td>文件夹描述</td></tr>
|
||
<tr><td>parentId</td><td>Long</td><td>否</td><td>父文件夹ID(可移动文件夹)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"更新成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"parentId"</span>: <span class="number">0</span>,
|
||
<span class="key">"name"</span>: <span class="string">"新文件夹名称"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"新描述"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T11:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 文件夹不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"文件夹不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"无权修改该文件夹"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders/{folderId}</span>
|
||
<span class="api-desc">删除文件夹</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>文件夹ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>说明</h4>
|
||
<p>删除文件夹前会检查文件夹是否为空,如果包含子文件夹或电影文件则拒绝删除。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>错误码</h4>
|
||
<table class="param-table">
|
||
<tr><th>code</th><th>说明</th></tr>
|
||
<tr><td>400</td><td>文件夹不为空或数据库操作失败</td></tr>
|
||
<tr><td>401</td><td>未登录或Token无效</td></tr>
|
||
<tr><td>403</td><td>无权访问该文件夹</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/drive/{driveId}/movies</span>
|
||
<span class="api-desc">创建电影(只传文件名)</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"title"</span>: <span class="string">"文件名.mp4"</span>, <span style="color:#718096">// 必填,文件名(同时作为电影标题)</span>
|
||
<span class="key">"folderId"</span>: <span class="number">0</span> <span style="color:#718096">// 可选,默认0(根目录)</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>参数说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>title</td><td>String</td><td>是</td><td>文件名(同时作为电影标题,后续通过 TMDB 刮削补充信息)</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>否</td><td>目标文件夹ID,默认0(根目录)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>说明</h4>
|
||
<p>只记录文件名,不上传文件数据。创建后通过 TMDB 刮削接口补充电影的海报、描述、评分等信息。自动检测 MIME 类型(mp4/mkv/avi → video/mp4)。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"创建成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">0</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影.mp4"</span>,
|
||
<span class="key">"originalName"</span>: <span class="string">"电影.mp4"</span>,
|
||
<span class="key">"mimeType"</span>: <span class="string">"video/mp4"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数缺失</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"文件名不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 网盘不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders/{folderId}/movies</span>
|
||
<span class="api-desc">查询电影文件列表</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>文件夹ID(0表示根目录)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||
<tr><td>pageNo</td><td>1</td><td>页码</td></tr>
|
||
<tr><td>pageSize</td><td>20</td><td>每页条数(最大100)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>list[].id</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>list[].title</td><td>String</td><td>电影名称</td></tr>
|
||
<tr><td>list[].originalName</td><td>String</td><td>原始文件名</td></tr>
|
||
<tr><td>list[].mimeType</td><td>String</td><td>MIME类型</td></tr>
|
||
<tr><td>list[].fileSize</td><td>Long</td><td>文件大小(字节)</td></tr>
|
||
<tr><td>list[].extension</td><td>String</td><td>文件扩展名</td></tr>
|
||
<tr><td>list[].minioUrl</td><td>String</td><td>MinIO文件访问URL</td></tr>
|
||
<tr><td>list[].rating</td><td>Integer</td><td>评分</td></tr>
|
||
<tr><td>list[].tags</td><td>String</td><td>标签</td></tr>
|
||
<tr><td>total</td><td>Integer</td><td>总数</td></tr>
|
||
<tr><td>pageNo</td><td>Integer</td><td>当前页码</td></tr>
|
||
<tr><td>pageSize</td><td>Integer</td><td>每页条数</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"total"</span>: <span class="number">5</span>,
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">20</span>,
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影.mp4"</span>,
|
||
<span class="key">"originalName"</span>: <span class="string">"电影.mp4"</span>,
|
||
<span class="key">"mimeType"</span>: <span class="string">"video/mp4"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">1073741824</span>,
|
||
<span class="key">"extension"</span>: <span class="string">"mp4"</span>,
|
||
<span class="key">"minioUrl"</span>: <span class="string">"http://..."</span>,
|
||
<span class="key">"rating"</span>: <span class="number">85</span>,
|
||
<span class="key">"tags"</span>: <span class="string">"动作,科幻"</span>
|
||
}
|
||
]
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 网盘不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"网盘不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/{driveId}/movies/{movieId}</span>
|
||
<span class="api-desc">查询电影文件详情</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>所属网盘ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>所属文件夹ID</td></tr>
|
||
<tr><td>title</td><td>String</td><td>电影名称</td></tr>
|
||
<tr><td>mimeType</td><td>String</td><td>MIME类型</td></tr>
|
||
<tr><td>fileSize</td><td>Long</td><td>文件大小(字节)</td></tr>
|
||
<tr><td>extension</td><td>String</td><td>文件扩展名</td></tr>
|
||
<tr><td>description</td><td>String</td><td>描述</td></tr>
|
||
<tr><td>maintainer</td><td>String</td><td>维护者</td></tr>
|
||
<tr><td>status</td><td>String</td><td>状态</td></tr>
|
||
<tr><td>scrapeType</td><td>String</td><td>刮削类型</td></tr>
|
||
<tr><td>scrapeStatus</td><td>String</td><td>刮削状态</td></tr>
|
||
<tr><td>rating</td><td>Integer|null</td><td>评分(刮削成功时返回)</td></tr>
|
||
<tr><td>tags</td><td>String</td><td>标签</td></tr>
|
||
<tr><td>casts</td><td>String|null</td><td>主演(刮削成功时返回)</td></tr>
|
||
<tr><td>directors</td><td>String|null</td><td>导演(刮削成功时返回)</td></tr>
|
||
<tr><td>posterUrl</td><td>String|null</td><td>海报URL(刮削成功时返回)</td></tr>
|
||
<tr><td>backdropUrl</td><td>String|null</td><td>背景图URL(刮削成功时返回)</td></tr>
|
||
<tr><td>createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>updateTime</td><td>String</td><td>更新时间</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例(刮削成功)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">0</span>,
|
||
<span class="key">"title"</span>: <span class="string">"阿凡达.mp4"</span>,
|
||
<span class="key">"mimeType"</span>: <span class="string">"video/mp4"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">1073741824</span>,
|
||
<span class="key">"extension"</span>: <span class="string">"mp4"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"电影描述"</span>,
|
||
<span class="key">"maintainer"</span>: <span class="string">"admin"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"scrapeType"</span>: <span class="string">"tmdb"</span>,
|
||
<span class="key">"scrapeStatus"</span>: <span class="string">"success"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">85</span>,
|
||
<span class="key">"tags"</span>: <span class="string">"动作,科幻"</span>,
|
||
<span class="key">"casts"</span>: <span class="string">"主演1,主演2"</span>,
|
||
<span class="key">"directors"</span>: <span class="string">"导演1"</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"http://..."</span>,
|
||
<span class="key">"backdropUrl"</span>: <span class="string">"http://..."</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例(未刮削/刮削失败)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">2</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">0</span>,
|
||
<span class="key">"title"</span>: <span class="string">"未刮削电影.mp4"</span>,
|
||
<span class="key">"mimeType"</span>: <span class="string">"video/mp4"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">536870912</span>,
|
||
<span class="key">"extension"</span>: <span class="string">"mp4"</span>,
|
||
<span class="key">"description"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"maintainer"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"scrapeType"</span>: <span class="string">"none"</span>,
|
||
<span class="key">"scrapeStatus"</span>: <span class="string">"pending"</span>,
|
||
<span class="key">"rating"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"tags"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"casts"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"directors"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"backdropUrl"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/drive/{driveId}/movies/{movieId}</span>
|
||
<span class="api-desc">更新电影文件信息</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体(部分更新,所有字段可选)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"title"</span>: <span class="string">"新电影名称"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"新描述"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">90</span>,
|
||
<span class="key">"tags"</span>: <span class="string">"新标签"</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"海报URL"</span>,
|
||
<span class="key">"backdropUrl"</span>: <span class="string">"背景图URL"</span>,
|
||
<span class="key">"casts"</span>: <span class="string">"主演列表"</span>,
|
||
<span class="key">"directors"</span>: <span class="string">"导演列表"</span>,
|
||
<span class="key">"resourceLibrary"</span>: <span class="string">"资源库来源"</span>,
|
||
<span class="key">"maintainer"</span>: <span class="string">"维护者"</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>参数说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>title</td><td>String</td><td>电影名称</td></tr>
|
||
<tr><td>description</td><td>String</td><td>描述</td></tr>
|
||
<tr><td>rating</td><td>Integer</td><td>评分 0-100</td></tr>
|
||
<tr><td>tags</td><td>String</td><td>标签</td></tr>
|
||
<tr><td>posterUrl</td><td>String</td><td>海报URL</td></tr>
|
||
<tr><td>backdropUrl</td><td>String</td><td>背景图URL</td></tr>
|
||
<tr><td>casts</td><td>String</td><td>主演(逗号分隔)</td></tr>
|
||
<tr><td>directors</td><td>String</td><td>导演(逗号分隔)</td></tr>
|
||
<tr><td>resourceLibrary</td><td>String</td><td>资源库来源</td></tr>
|
||
<tr><td>maintainer</td><td>String</td><td>维护者</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>说明</h4>
|
||
<p>用于 TMDB 刮削有误时,用户可自定义修改文件名等信息。所有字段均为可选,只更新传入的字段。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"更新成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"新电影名称"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">90</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T11:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"无权修改该电影"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/drive/{driveId}/movies/{movieId}</span>
|
||
<span class="api-desc">删除电影文件</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘ID</td></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>说明</h4>
|
||
<p>逻辑删除(软删除),删除后自动更新网盘和文件夹的统计信息。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"删除成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 权限不足</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">403</span>,
|
||
<span class="key">"message"</span>: <span class="string">"无权删除该电影"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 重复电影检查 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/movies/duplicates</span>
|
||
<span class="api-desc">检查重复电影</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>查询当前用户所有网盘下所有文件夹中的电影,按标题(title)分组找出重复的电影。返回每组重复电影的详细信息,包括所在文件夹的完整路径,前端可让用户选择删除其中一个。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"duplicateGroups"</span>: [
|
||
{
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>,
|
||
<span class="key">"count"</span>: <span class="number">2</span>,
|
||
<span class="key">"movies"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">10</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">5</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>,
|
||
<span class="key">"originalName"</span>: <span class="string">"电影名称.mp4"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">1073741824</span>,
|
||
<span class="key">"extension"</span>: <span class="string">"mp4"</span>,
|
||
<span class="key">"category"</span>: <span class="string">"sci-fi"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">85</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2025-01-01T10:00:00"</span>,
|
||
<span class="key">"folderPath"</span>: <span class="string">"我的电影库/科幻/2024"</span>
|
||
},
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">20</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">8</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>,
|
||
<span class="key">"originalName"</span>: <span class="string">"电影名称.mkv"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">2147483648</span>,
|
||
<span class="key">"extension"</span>: <span class="string">"mkv"</span>,
|
||
<span class="key">"category"</span>: <span class="string">"sci-fi"</span>,
|
||
<span class="key">"rating"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2025-02-01T15:00:00"</span>,
|
||
<span class="key">"folderPath"</span>: <span class="string">"我的电影库/动作"</span>
|
||
}
|
||
]
|
||
}
|
||
],
|
||
<span class="key">"totalGroups"</span>: <span class="number">1</span>,
|
||
<span class="key">"totalDuplicates"</span>: <span class="number">2</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>返回字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>duplicateGroups</td><td>Array</td><td>重复电影分组列表</td></tr>
|
||
<tr><td>duplicateGroups[].title</td><td>String</td><td>重复的电影标题</td></tr>
|
||
<tr><td>duplicateGroups[].count</td><td>Integer</td><td>该标题下的重复数量</td></tr>
|
||
<tr><td>duplicateGroups[].movies</td><td>Array</td><td>该标题下的所有电影记录</td></tr>
|
||
<tr><td>duplicateGroups[].movies[].id</td><td>Long</td><td>电影ID(可用于删除接口)</td></tr>
|
||
<tr><td>duplicateGroups[].movies[].folderPath</td><td>String</td><td>所在文件夹完整路径,如"网盘名/文件夹1/文件夹2"</td></tr>
|
||
<tr><td>totalGroups</td><td>Integer</td><td>重复组总数</td></tr>
|
||
<tr><td>totalDuplicates</td><td>Integer</td><td>涉及重复的电影总数</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>错误码</h4>
|
||
<table class="param-table">
|
||
<tr><th>code</th><th>说明</th></tr>
|
||
<tr><td>401</td><td>未登录或 Token 无效</td></tr>
|
||
<tr><td>500</td><td>服务器内部错误</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Icon服务 -->
|
||
<div class="section" id="icon">
|
||
<div class="service-card">
|
||
<h2>Icon 管理服务</h2>
|
||
<span class="port">wangpan-drive-service : 8082</span>
|
||
</div>
|
||
|
||
<!-- 创建 Icon -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/icon</span>
|
||
<span class="api-desc">创建 Icon</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体(JSON)</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>name</td><td>String</td><td>是</td><td>Icon 名称(最大100字符)</td></tr>
|
||
<tr><td>url</td><td>String</td><td>条件必填</td><td>外网链接 URL(最大500字符,linkType为external或both时必填)</td></tr>
|
||
<tr><td>internalUrl</td><td>String</td><td>条件必填</td><td>内网链接 URL(最大500字符,linkType为internal或both时必填)</td></tr>
|
||
<tr><td>linkType</td><td>String</td><td>否</td><td>链接类型:external(仅外网,默认)/ internal(仅内网)/ both(内外网)</td></tr>
|
||
<tr><td>description</td><td>String</td><td>否</td><td>Icon 描述(最大500字符)</td></tr>
|
||
<tr><td>sortOrder</td><td>Integer</td><td>否</td><td>排序值,数字越小越靠前,默认0</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求示例(仅外网)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"name"</span>: <span class="string">"百度"</span>,
|
||
<span class="key">"url"</span>: <span class="string">"https://www.example.com"</span>,
|
||
<span class="key">"linkType"</span>: <span class="string">"external"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"搜索引擎"</span>,
|
||
<span class="key">"sortOrder"</span>: <span class="number">1</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求示例(内外网都有)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"name"</span>: <span class="string">"内部系统"</span>,
|
||
<span class="key">"url"</span>: <span class="string">"https://system.example.com"</span>,
|
||
<span class="key">"internalUrl"</span>: <span class="string">"http://192.168.x.x:8080"</span>,
|
||
<span class="key">"linkType"</span>: <span class="string">"both"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"内部管理系统"</span>,
|
||
<span class="key">"sortOrder"</span>: <span class="number">2</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应(200)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"创建成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"百度"</span>,
|
||
<span class="key">"url"</span>: <span class="string">"https://www.example.com"</span>,
|
||
<span class="key">"internalUrl"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"linkType"</span>: <span class="string">"external"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"搜索引擎"</span>,
|
||
<span class="key">"sortOrder"</span>: <span class="number">1</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/icons/2/1718265600000.png"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-13T16:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 查询 Icon 列表 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/icon</span>
|
||
<span class="api-desc">查询当前用户的 Icon 列表</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>查询当前登录用户的所有 Icon,按 sortOrder 升序、createTime 降序排列。每个用户只能看到自己创建的 Icon。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应(200)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"百度"</span>,
|
||
<span class="key">"url"</span>: <span class="string">"https://www.example.com"</span>,
|
||
<span class="key">"internalUrl"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"linkType"</span>: <span class="string">"external"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"搜索引擎"</span>,
|
||
<span class="key">"sortOrder"</span>: <span class="number">1</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/icons/2/1718265600000.png"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-13T16:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-13T16:00:00"</span>
|
||
}
|
||
],
|
||
<span class="key">"total"</span>: <span class="number">1</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 查询单个 Icon -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/icon/{id}</span>
|
||
<span class="api-desc">查询单个 Icon 详情</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>Icon ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应(200)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"百度"</span>,
|
||
<span class="key">"url"</span>: <span class="string">"https://www.example.com"</span>,
|
||
<span class="key">"internalUrl"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"linkType"</span>: <span class="string">"external"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"搜索引擎"</span>,
|
||
<span class="key">"sortOrder"</span>: <span class="number">1</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/icons/2/1718265600000.png"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-13T16:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-13T16:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — Icon 不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"Icon 不存在或无权访问"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 无效"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 更新 Icon -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/icon/{id}</span>
|
||
<span class="api-desc">更新 Icon</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>Icon ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体(JSON,所有字段可选)</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>name</td><td>String</td><td>Icon 名称</td></tr>
|
||
<tr><td>url</td><td>String</td><td>外网链接</td></tr>
|
||
<tr><td>internalUrl</td><td>String</td><td>内网链接</td></tr>
|
||
<tr><td>linkType</td><td>String</td><td>链接类型:external / internal / both</td></tr>
|
||
<tr><td>description</td><td>String</td><td>Icon 描述</td></tr>
|
||
<tr><td>sortOrder</td><td>Integer</td><td>排序值</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"name"</span>: <span class="string">"新名称"</span>,
|
||
<span class="key">"url"</span>: <span class="string">"https://new-url.com"</span>,
|
||
<span class="key">"internalUrl"</span>: <span class="string">"http://192.168.x.x:8080"</span>,
|
||
<span class="key">"linkType"</span>: <span class="string">"both"</span>,
|
||
<span class="key">"sortOrder"</span>: <span class="number">2</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应(200)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"更新成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"新名称"</span>,
|
||
<span class="key">"url"</span>: <span class="string">"https://new-url.com"</span>,
|
||
<span class="key">"internalUrl"</span>: <span class="string">"http://192.168.x.x:8080"</span>,
|
||
<span class="key">"linkType"</span>: <span class="string">"both"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"搜索引擎"</span>,
|
||
<span class="key">"sortOrder"</span>: <span class="number">2</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/icons/2/1718265600000.png"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-13T16:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-13T17:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>错误码</h4>
|
||
<table class="param-table">
|
||
<tr><th>code</th><th>说明</th></tr>
|
||
<tr><td>401</td><td>未登录或 Token 无效</td></tr>
|
||
<tr><td>500</td><td>Icon 不存在或无权修改</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 删除 Icon -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/icon/{id}</span>
|
||
<span class="api-desc">删除 Icon</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>Icon ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应(200)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"删除成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>错误码</h4>
|
||
<table class="param-table">
|
||
<tr><th>code</th><th>说明</th></tr>
|
||
<tr><td>401</td><td>未登录或 Token 无效</td></tr>
|
||
<tr><td>500</td><td>Icon 不存在或无权删除</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 上传自定义图标 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/icon/{id}/logo</span>
|
||
<span class="api-desc">上传自定义图标图片</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>上传自定义图标图片替换自动抓取的 logo。上传后会自动删除旧的 MinIO 图片,防止硬盘空间浪费。系统每 24 小时自动清理无用图片。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>Icon ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求格式</h4>
|
||
<p><code>multipart/form-data</code></p>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>file</td><td>File</td><td>是</td><td>图片文件(png/jpg/jpeg/gif/webp/ico,最大 1MB)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求示例(curl)</h4>
|
||
<div class="code-block">curl -X POST http://localhost:8888/api/icon/1/logo \
|
||
-H "Authorization: Bearer <token>" \
|
||
-F "file=@/path/to/icon.png"</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应(200)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"图标上传成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"name"</span>: <span class="string">"百度"</span>,
|
||
<span class="key">"logoUrl"</span>: <span class="string">"http://your-server:39000/wangpan/icons/2/1718265600000.png"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-15T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 文件为空</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"上传文件为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 文件格式不支持</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"不支持的文件格式,仅支持 png/jpg/jpeg/gif/webp/ico"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 文件超过 1MB</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"文件大小超过限制(最大 1MB)"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — Icon 不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"Icon 不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TMDB服务 -->
|
||
<div class="section" id="tmdb">
|
||
<div class="service-card">
|
||
<h2>TMDB 刮削服务</h2>
|
||
<span class="port">wangpan-tmdb-service : 8083</span>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/movies/recommend</span>
|
||
<span class="api-desc">今日推荐(每天固定3部电影)</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>从当前用户的电影库中随机抽取3部电影用于首页展示。</p>
|
||
<p><strong>推荐规则:</strong></p>
|
||
<ul>
|
||
<li>从 00:00 到 23:59 返回固定的3部电影</li>
|
||
<li>当天不管查询多少次都返回相同的结果</li>
|
||
<li>第二天 00:00 后重新随机挑选3部</li>
|
||
<li>推荐结果缓存在 wp_daily_recommend 表中</li>
|
||
</ul>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">10</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称1"</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"海报URL"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">85</span>,
|
||
<span class="key">"category"</span>: <span class="string">"动作"</span>,
|
||
<span class="key">"tags"</span>: <span class="string">"标签1,标签2"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2025-01-01T10:00:00"</span>
|
||
},
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">20</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称2"</span>,
|
||
...
|
||
},
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">30</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称3"</span>,
|
||
...
|
||
}
|
||
],
|
||
<span class="key">"count"</span>: <span class="number">3</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>返回字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>list</td><td>Array</td><td>推荐的电影列表(最多3部)</td></tr>
|
||
<tr><td>list[].id</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>list[].title</td><td>String</td><td>电影标题</td></tr>
|
||
<tr><td>list[].posterUrl</td><td>String</td><td>海报URL</td></tr>
|
||
<tr><td>list[].rating</td><td>Integer</td><td>评分(0-100)</td></tr>
|
||
<tr><td>list[].category</td><td>String</td><td>分类</td></tr>
|
||
<tr><td>list[].tags</td><td>String</td><td>标签</td></tr>
|
||
<tr><td>list[].createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>count</td><td>Integer</td><td>实际返回的电影数量(可能小于3)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 服务器内部错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"服务器内部错误,请稍后重试"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/movies</span>
|
||
<span class="api-desc">电影列表查询(按文件夹分组,每组最多10条)</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>从 <code>wp_drive_movie</code> 表查询当前用户所有电影,按文件夹分组返回,每个文件夹最多返回 10 条电影数据。支持分页:pageNo 控制文件夹分页。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>keyword</td><td>String</td><td>否</td><td>搜索关键词</td></tr>
|
||
<tr><td>status</td><td>String</td><td>否</td><td>电影状态:active(正常)/ deleted(已删除)</td></tr>
|
||
<tr><td>scrapeType</td><td>String</td><td>否</td><td>刮削类型:tmdb / manual / none</td></tr>
|
||
<tr><td>sortBy</td><td>String</td><td>否</td><td>排序字段(如 createTime、rating、title)</td></tr>
|
||
<tr><td>sortOrder</td><td>String</td><td>否</td><td>排序方向:asc / desc</td></tr>
|
||
<tr><td>pageNo</td><td>int</td><td>否</td><td>页码,默认 1</td></tr>
|
||
<tr><td>pageSize</td><td>int</td><td>否</td><td>每页条数(文件夹数),默认 20</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>totalFolders</td><td>Integer</td><td>文件夹总数</td></tr>
|
||
<tr><td>pageNo</td><td>Integer</td><td>当前页码</td></tr>
|
||
<tr><td>pageSize</td><td>Integer</td><td>每页条数</td></tr>
|
||
<tr><td>folderGroups[].folderId</td><td>Long</td><td>文件夹ID</td></tr>
|
||
<tr><td>folderGroups[].movieCount</td><td>Integer</td><td>该文件夹下返回的电影数量</td></tr>
|
||
<tr><td>folderGroups[].movies[].id</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>folderGroups[].movies[].driveId</td><td>Long</td><td>所属网盘ID</td></tr>
|
||
<tr><td>folderGroups[].movies[].folderId</td><td>Long</td><td>所属文件夹ID</td></tr>
|
||
<tr><td>folderGroups[].movies[].title</td><td>String</td><td>电影名称</td></tr>
|
||
<tr><td>folderGroups[].movies[].originalName</td><td>String</td><td>原始文件名</td></tr>
|
||
<tr><td>folderGroups[].movies[].mimeType</td><td>String</td><td>MIME类型</td></tr>
|
||
<tr><td>folderGroups[].movies[].fileSize</td><td>Long</td><td>文件大小(字节)</td></tr>
|
||
<tr><td>folderGroups[].movies[].extension</td><td>String</td><td>文件扩展名</td></tr>
|
||
<tr><td>folderGroups[].movies[].description</td><td>String</td><td>描述</td></tr>
|
||
<tr><td>folderGroups[].movies[].scrapeStatus</td><td>String</td><td>刮削状态</td></tr>
|
||
<tr><td>folderGroups[].movies[].rating</td><td>Integer</td><td>评分(0-100)</td></tr>
|
||
<tr><td>folderGroups[].movies[].tags</td><td>String</td><td>标签</td></tr>
|
||
<tr><td>folderGroups[].movies[].casts</td><td>String</td><td>主演(逗号分隔,TMDB刮削)</td></tr>
|
||
<tr><td>folderGroups[].movies[].directors</td><td>String</td><td>导演(逗号分隔,TMDB刮削)</td></tr>
|
||
<tr><td>folderGroups[].movies[].posterUrl</td><td>String</td><td>竖版海报URL(2:3 比例)</td></tr>
|
||
<tr><td>folderGroups[].movies[].backdropUrl</td><td>String</td><td>横版背景图URL(16:9 比例)</td></tr>
|
||
<tr><td>folderGroups[].movies[].minioUrl</td><td>String</td><td>MinIO 文件访问URL</td></tr>
|
||
<tr><td>folderGroups[].movies[].createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>folderGroups[].movies[].updateTime</td><td>String</td><td>更新时间</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"totalFolders"</span>: <span class="number">5</span>,
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">20</span>,
|
||
<span class="key">"folderGroups"</span>: [
|
||
{
|
||
<span class="key">"folderId"</span>: <span class="number">3</span>,
|
||
<span class="key">"movieCount"</span>: <span class="number">2</span>,
|
||
<span class="key">"movies"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">3</span>,
|
||
<span class="key">"title"</span>: <span class="string">"肖申克的救赎"</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/poster_xxx.jpg"</span>,
|
||
<span class="key">"backdropUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/backdrop_xxx.jpg"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">93</span>,
|
||
...
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"请求参数错误"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/movies/all</span>
|
||
<span class="api-desc">查询所有电影(不分文件夹,直接分页)</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>查询当前用户所有网盘下所有未删除的电影,不进行分类直接分页返回。适用于大数据量场景(1万+),内置5分钟缓存机制,支持关键词搜索和多字段排序。</p>
|
||
<p><strong>设计特点:</strong></p>
|
||
<ul>
|
||
<li>不进行分类,直接分页查询所有电影</li>
|
||
<li>支持关键词搜索(标题模糊匹配)</li>
|
||
<li>支持排序(按创建时间、更新时间、评分、标题)</li>
|
||
<li>内置5分钟缓存,适用于1万+数据量场景</li>
|
||
<li>最大每页100条,防止单次查询数据量过大</li>
|
||
<li><strong>每日随机刷新</strong>:第1页前20条数据每日随机刷新(无关键词搜索时生效),增加内容变化性,其余数据按排序规则保持不变</li>
|
||
</ul>
|
||
<p><strong>每日随机刷新机制:</strong></p>
|
||
<ul>
|
||
<li>触发条件:查询第1页且无关键词搜索时</li>
|
||
<li>随机数量:前20条数据从所有电影中随机选取</li>
|
||
<li>缓存策略:缓存Key包含日期因子,每日自动刷新</li>
|
||
<li>其余数据:第21条及以后按指定排序规则返回</li>
|
||
<li>关键词搜索时:不触发随机,正常返回搜索结果</li>
|
||
</ul>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>keyword</td><td>String</td><td>否</td><td>搜索关键词(电影标题模糊匹配)</td></tr>
|
||
<tr><td>sortBy</td><td>String</td><td>否</td><td>排序字段:createTime(创建时间)/ updateTime(更新时间)/ rating(评分)/ title(标题)</td></tr>
|
||
<tr><td>sortOrder</td><td>String</td><td>否</td><td>排序方向:asc(升序)/ desc(降序),默认desc</td></tr>
|
||
<tr><td>pageNo</td><td>int</td><td>否</td><td>页码,默认 1</td></tr>
|
||
<tr><td>pageSize</td><td>int</td><td>否</td><td>每页条数,默认 50,最大 100</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>total</td><td>Long</td><td>电影总数</td></tr>
|
||
<tr><td>pages</td><td>Long</td><td>总页数</td></tr>
|
||
<tr><td>pageNo</td><td>Long</td><td>当前页码</td></tr>
|
||
<tr><td>pageSize</td><td>Long</td><td>每页条数</td></tr>
|
||
<tr><td>list[].id</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>list[].driveId</td><td>Long</td><td>所属网盘ID</td></tr>
|
||
<tr><td>list[].folderId</td><td>Long</td><td>所属文件夹ID</td></tr>
|
||
<tr><td>list[].title</td><td>String</td><td>电影名称</td></tr>
|
||
<tr><td>list[].originalName</td><td>String</td><td>原始文件名</td></tr>
|
||
<tr><td>list[].mimeType</td><td>String</td><td>MIME类型</td></tr>
|
||
<tr><td>list[].fileSize</td><td>Long</td><td>文件大小(字节)</td></tr>
|
||
<tr><td>list[].extension</td><td>String</td><td>文件扩展名</td></tr>
|
||
<tr><td>list[].description</td><td>String</td><td>描述</td></tr>
|
||
<tr><td>list[].scrapeStatus</td><td>String</td><td>刮削状态:pending/scraping/success/failed</td></tr>
|
||
<tr><td>list[].rating</td><td>Integer</td><td>评分(0-100)</td></tr>
|
||
<tr><td>list[].tags</td><td>String</td><td>标签</td></tr>
|
||
<tr><td>list[].casts</td><td>String</td><td>主演(逗号分隔,TMDB刮削)</td></tr>
|
||
<tr><td>list[].directors</td><td>String</td><td>导演(逗号分隔,TMDB刮削)</td></tr>
|
||
<tr><td>list[].posterUrl</td><td>String</td><td>竖版海报URL(2:3 比例)</td></tr>
|
||
<tr><td>list[].backdropUrl</td><td>String</td><td>横版背景图URL(16:9 比例)</td></tr>
|
||
<tr><td>list[].minioUrl</td><td>String</td><td>MinIO 文件访问URL</td></tr>
|
||
<tr><td>list[].createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>list[].updateTime</td><td>String</td><td>更新时间</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"total"</span>: <span class="number">12580</span>,
|
||
<span class="key">"pages"</span>: <span class="number">252</span>,
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">50</span>,
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">3</span>,
|
||
<span class="key">"title"</span>: <span class="string">"肖申克的救赎"</span>,
|
||
<span class="key">"originalName"</span>: <span class="string">"The.Shawshank.Redemption.1994.mkv"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">8589934592</span>,
|
||
<span class="key">"description"</span>: <span class="string">"一场冤案让银行家安迪入狱,他在肖申克监狱用20年时间挖出一条自由之路..."</span>,
|
||
<span class="key">"scrapeStatus"</span>: <span class="string">"success"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">93</span>,
|
||
<span class="key">"casts"</span>: <span class="string">"蒂姆·罗宾斯,摩根·弗里曼"</span>,
|
||
<span class="key">"directors"</span>: <span class="string">"弗兰克·德拉邦特"</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/poster_xxx.jpg"</span>,
|
||
<span class="key">"backdropUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/backdrop_xxx.jpg"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-20 10:30:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-27 14:20:00"</span>
|
||
}
|
||
]
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例 — 命中缓存</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功(缓存)"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"total"</span>: <span class="number">12580</span>,
|
||
<span class="key">"pages"</span>: <span class="number">252</span>,
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">50</span>,
|
||
<span class="key">"list"</span>: [...]
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 查询失败</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询失败:数据库连接异常"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>前端调用示例</h4>
|
||
<div class="code-block">// 查询第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);
|
||
});</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/movies/{id}</span>
|
||
<span class="api-desc">电影详情查询</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>查询单部电影的完整详情信息,包括基本信息、海报、评分、简介、导演、主演等。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>所属网盘ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>所属文件夹ID</td></tr>
|
||
<tr><td>title</td><td>String</td><td>电影名称</td></tr>
|
||
<tr><td>originalName</td><td>String</td><td>原始文件名</td></tr>
|
||
<tr><td>mimeType</td><td>String</td><td>MIME类型</td></tr>
|
||
<tr><td>fileSize</td><td>Long</td><td>文件大小(字节)</td></tr>
|
||
<tr><td>extension</td><td>String</td><td>文件扩展名</td></tr>
|
||
<tr><td>description</td><td>String</td><td>电影描述</td></tr>
|
||
<tr><td>scrapeStatus</td><td>String</td><td>刮削状态:pending/scraping/success/failed</td></tr>
|
||
<tr><td>rating</td><td>Integer</td><td>评分(0-100)</td></tr>
|
||
<tr><td>casts</td><td>String</td><td>主演(逗号分隔,TMDB刮削)</td></tr>
|
||
<tr><td>directors</td><td>String</td><td>导演(逗号分隔,TMDB刮削)</td></tr>
|
||
<tr><td>posterUrl</td><td>String</td><td>竖版海报URL(2:3 比例)</td></tr>
|
||
<tr><td>backdropUrl</td><td>String</td><td>横版背景图URL(16:9 比例)</td></tr>
|
||
<tr><td>tags</td><td>String</td><td>标签(逗号分隔)</td></tr>
|
||
<tr><td>createTime</td><td>String</td><td>创建时间</td></tr>
|
||
<tr><td>updateTime</td><td>String</td><td>更新时间</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"driveId"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderId"</span>: <span class="number">3</span>,
|
||
<span class="key">"title"</span>: <span class="string">"肖申克的救赎"</span>,
|
||
<span class="key">"originalName"</span>: <span class="string">"肖申克的救赎.mp4"</span>,
|
||
<span class="key">"mimeType"</span>: <span class="string">"video/mp4"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">2147483648</span>,
|
||
<span class="key">"extension"</span>: <span class="string">"mp4"</span>,
|
||
<span class="key">"description"</span>: <span class="string">"电影描述"</span>,
|
||
<span class="key">"scrapeStatus"</span>: <span class="string">"success"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">93</span>,
|
||
<span class="key">"casts"</span>: <span class="string">"Tim Robbins, Morgan Freeman"</span>,
|
||
<span class="key">"directors"</span>: <span class="string">"Frank Darabont"</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/poster_xxx.jpg"</span>,
|
||
<span class="key">"backdropUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/backdrop_xxx.jpg"</span>,
|
||
<span class="key">"tags"</span>: <span class="string">"剧情,犯罪"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2025-01-01T10:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2025-01-15T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在或无权访问"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/movies/{id}/scrape-status</span>
|
||
<span class="api-desc">查询单部电影刮削状态</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>查询单部电影的刮削状态,返回三种状态:刮削中(scraping)、刮削失败(failed)、刮削成功(success)。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>title</td><td>String</td><td>电影名称</td></tr>
|
||
<tr><td>status</td><td>String</td><td>刮削状态:scraping / failed / success / unknown</td></tr>
|
||
<tr><td>message</td><td>String</td><td>状态描述信息</td></tr>
|
||
<tr><td>error</td><td>String</td><td>失败原因(仅 status=failed 时返回)</td></tr>
|
||
<tr><td>posterUrl</td><td>String</td><td>竖版海报URL(仅 status=success 时返回)</td></tr>
|
||
<tr><td>backdropUrl</td><td>String</td><td>横版背景图URL(仅 status=success 时返回)</td></tr>
|
||
<tr><td>rating</td><td>Integer</td><td>评分(仅 status=success 时返回)</td></tr>
|
||
<tr><td>category</td><td>String</td><td>分类(仅 status=success 时返回)</td></tr>
|
||
<tr><td>tags</td><td>String</td><td>标签(仅 status=success 时返回)</td></tr>
|
||
<tr><td>casts</td><td>String</td><td>主演(仅 status=success 时返回)</td></tr>
|
||
<tr><td>directors</td><td>String</td><td>导演(仅 status=success 时返回)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例 - 刮削中</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"movieId"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"scraping"</span>,
|
||
<span class="key">"message"</span>: <span class="string">"正在刮削中,请稍候..."</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例 - 刮削失败</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"movieId"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"failed"</span>,
|
||
<span class="key">"message"</span>: <span class="string">"刮削失败"</span>,
|
||
<span class="key">"error"</span>: <span class="string">"TMDB API 请求失败"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例 - 刮削成功</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"movieId"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"success"</span>,
|
||
<span class="key">"message"</span>: <span class="string">"刮削成功"</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/poster_xxx.jpg"</span>,
|
||
<span class="key">"backdropUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/1/backdrop_xxx.jpg"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">85</span>,
|
||
<span class="key">"category"</span>: <span class="string">"动作"</span>,
|
||
<span class="key">"tags"</span>: <span class="string">"动作,科幻"</span>,
|
||
<span class="key">"casts"</span>: <span class="string">"主演1,主演2"</span>,
|
||
<span class="key">"directors"</span>: <span class="string">"导演1"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在或无权访问"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/movies</span>
|
||
<span class="api-desc">创建电影</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>创建新的电影记录,title 为必填字段。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体(所有字段可选,title 必填)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>, <span style="color:#718096">// 必填</span>
|
||
<span class="key">"maintainer"</span>: <span class="string">"维护者"</span>, <span style="color:#718096">// 可选</span>
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>, <span style="color:#718096">// 可选:active / deleted</span>
|
||
<span class="key">"category"</span>: <span class="string">"动作"</span>, <span style="color:#718096">// 可选</span>
|
||
<span class="key">"scrapeType"</span>: <span class="string">"tmdb"</span>, <span style="color:#718096">// 可选:tmdb / manual / none</span>
|
||
<span class="key">"rating"</span>: <span class="number">85</span>, <span style="color:#718096">// 可选:0-100</span>
|
||
<span class="key">"posterUrl"</span>: <span class="string">"海报URL"</span>, <span style="color:#718096">// 可选</span>
|
||
<span class="key">"tags"</span>: <span class="string">"标签1,标签2"</span> <span style="color:#718096">// 可选</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"创建成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"电影名称"</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2025-01-01T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 缺少必填字段</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影名称不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method PUT">PUT</span>
|
||
<span class="api-path">/api/movies/{id}</span>
|
||
<span class="api-desc">更新电影</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>更新指定电影的信息,支持部分更新。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体(部分更新,所有字段可选)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"title"</span>: <span class="string">"新电影名称"</span>,
|
||
<span class="key">"maintainer"</span>: <span class="string">"新维护者"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"category"</span>: <span class="string">"新分类"</span>,
|
||
<span class="key">"scrapeType"</span>: <span class="string">"tmdb"</span>,
|
||
<span class="key">"rating"</span>: <span class="number">90</span>,
|
||
<span class="key">"posterUrl"</span>: <span class="string">"新海报URL"</span>,
|
||
<span class="key">"tags"</span>: <span class="string">"新标签"</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"更新成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"新电影名称"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2025-01-15T10:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/movies/{id}</span>
|
||
<span class="api-desc">删除电影</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>删除指定的电影记录。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"删除成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/movies/batch-delete</span>
|
||
<span class="api-desc">批量删除电影</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>批量删除多个电影记录。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{ <span class="key">"ids"</span>: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] }</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"批量删除成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"deletedCount"</span>: <span class="number">3</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 参数错误</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">400</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影ID列表不能为空"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/movies/stats</span>
|
||
<span class="api-desc">电影统计</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>获取当前用户的电影数量统计,按刮削类型和分类分组。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"totalCount"</span>: <span class="number">10</span>,
|
||
<span class="key">"scrapedCount"</span>: <span class="number">8</span>,
|
||
<span class="key">"pendingCount"</span>: <span class="number">2</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/tmdb/key</span>
|
||
<span class="api-desc">获取 TMDB API Key</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>获取当前用户配置的 TMDB API Key。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"tmdbApiKey"</span>: <span class="string">"your_api_key_here"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/tmdb/key</span>
|
||
<span class="api-desc">设置 TMDB API Key</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{ <span class="key">"tmdbApiKey"</span>: <span class="string">"你的API Key"</span> }</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/tmdb-scraper/scrape</span>
|
||
<span class="api-desc">单部电影刮削</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>手动触发单部电影刮削,自动匹配TMDB最佳结果。刮削完成后会更新电影的简介、导演、主演、海报等信息。<strong>注意:两次请求需间隔5秒,防止频繁点击。</strong></p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求方式</h4>
|
||
<p>支持两种方式传递 movieId:</p>
|
||
<p>1. <strong>URL 查询参数</strong>:<code>?movieId=1</code></p>
|
||
<p>2. <strong>请求体 JSON</strong>:</p>
|
||
<div class="code-block">{ <span class="key">"movieId"</span>: <span class="number">1</span> }</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>userId</td><td>Long</td><td>用户ID</td></tr>
|
||
<tr><td>tmdbId</td><td>Long</td><td>TMDB电影ID</td></tr>
|
||
<tr><td>title</td><td>String</td><td>电影标题(来自TMDB)</td></tr>
|
||
<tr><td>originalTitle</td><td>String</td><td>原始标题(外文)</td></tr>
|
||
<tr><td>releaseDate</td><td>String</td><td>上映日期</td></tr>
|
||
<tr><td>posterPath</td><td>String</td><td>TMDB海报路径</td></tr>
|
||
<tr><td>backdropPath</td><td>String</td><td>TMDB背景图路径</td></tr>
|
||
<tr><td>localPosterUrl</td><td>String</td><td>本地竖版海报URL(MinIO)</td></tr>
|
||
<tr><td>localBackdropUrl</td><td>String</td><td>本地横版背景图URL(MinIO)</td></tr>
|
||
<tr><td>tmdbRating</td><td>Integer</td><td>TMDB评分(0-100分制)</td></tr>
|
||
<tr><td>voteCount</td><td>Integer</td><td>投票数</td></tr>
|
||
<tr><td>runtime</td><td>Integer</td><td>片长(分钟)</td></tr>
|
||
<tr><td>genres</td><td>String</td><td>类型(逗号分隔)</td></tr>
|
||
<tr><td>directors</td><td>String</td><td>导演(逗号分隔,最多2位)</td></tr>
|
||
<tr><td>casts</td><td>String</td><td>主演(逗号分隔,最多5位)</td></tr>
|
||
<tr><td>originalLanguage</td><td>String</td><td>原始语言</td></tr>
|
||
<tr><td>productionCountries</td><td>String</td><td>制片国家(逗号分隔)</td></tr>
|
||
<tr><td>cacheTime</td><td>String</td><td>缓存时间</td></tr>
|
||
<tr><td>status</td><td>String</td><td>缓存状态</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"刮削成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"movieId"</span>: <span class="number">1</span>,
|
||
<span class="key">"userId"</span>: <span class="number">1</span>,
|
||
<span class="key">"tmdbId"</span>: <span class="number">597</span>,
|
||
<span class="key">"title"</span>: <span class="string">"泰坦尼克号"</span>,
|
||
<span class="key">"originalTitle"</span>: <span class="string">"Titanic"</span>,
|
||
<span class="key">"releaseDate"</span>: <span class="string">"1997-11-18"</span>,
|
||
<span class="key">"posterPath"</span>: <span class="string">"/9xjZS2rlVxm8SFx8kPC3JIGCOY.jpg"</span>,
|
||
<span class="key">"backdropPath"</span>: <span class="string">"/rTh4K5uw9HypmpGslcKd4QfHl93.jpg"</span>,
|
||
<span class="key">"localPosterUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/poster_1_597.jpg"</span>,
|
||
<span class="key">"localBackdropUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/backdrop_1_597.jpg"</span>,
|
||
<span class="key">"tmdbRating"</span>: <span class="number">78</span>,
|
||
<span class="key">"voteCount"</span>: <span class="number">24563</span>,
|
||
<span class="key">"runtime"</span>: <span class="number">194</span>,
|
||
<span class="key">"genres"</span>: <span class="string">"剧情, 爱情, 灾难"</span>,
|
||
<span class="key">"directors"</span>: <span class="string">"James Cameron"</span>,
|
||
<span class="key">"casts"</span>: <span class="string">"Leonardo DiCaprio, Kate Winslet, Billy Zane, Kathy Bates, Frances Fisher"</span>,
|
||
<span class="key">"originalLanguage"</span>: <span class="string">"en"</span>,
|
||
<span class="key">"productionCountries"</span>: <span class="string">"United States of America"</span>,
|
||
<span class="key">"cacheTime"</span>: <span class="string">"2026-06-26T16:30:00"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 操作过于频繁</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"操作过于频繁,请等待 3 秒后再试"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未找到匹配结果</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"TMDB未找到匹配的电影: xxx"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/tmdb-scraper/search</span>
|
||
<span class="api-desc">搜索TMDB电影(返回所有匹配结果供前端选择)</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>根据电影ID自动获取标题,搜索TMDB返回所有匹配结果。如果查询出多条结果,前端应展示列表让用户选择。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>是</td><td>电影ID(自动获取标题搜索)</td></tr>
|
||
<tr><td>keyword</td><td>String</td><td>否</td><td>自定义搜索关键词(不传则使用电影标题)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>keyword</td><td>String</td><td>搜索关键词</td></tr>
|
||
<tr><td>total</td><td>Integer</td><td>搜索结果总数</td></tr>
|
||
<tr><td>list[].tmdbId</td><td>Long</td><td>TMDB电影ID(用于确认刮削)</td></tr>
|
||
<tr><td>list[].title</td><td>String</td><td>电影标题</td></tr>
|
||
<tr><td>list[].originalTitle</td><td>String</td><td>原始标题</td></tr>
|
||
<tr><td>list[].releaseDate</td><td>String</td><td>上映日期</td></tr>
|
||
<tr><td>list[].posterPath</td><td>String</td><td>海报图片URL</td></tr>
|
||
<tr><td>list[].voteAverage</td><td>Double</td><td>评分</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"movieId"</span>: <span class="number">23</span>,
|
||
<span class="key">"keyword"</span>: <span class="string">"泰坦尼克号"</span>,
|
||
<span class="key">"total"</span>: <span class="number">3</span>,
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"tmdbId"</span>: <span class="number">597</span>,
|
||
<span class="key">"title"</span>: <span class="string">"泰坦尼克号"</span>,
|
||
<span class="key">"originalTitle"</span>: <span class="string">"Titanic"</span>,
|
||
<span class="key">"releaseDate"</span>: <span class="string">"1997-11-18"</span>,
|
||
<span class="key">"posterPath"</span>: <span class="string">"https://image.tmdb.org/t/p/w500/xxx.jpg"</span>,
|
||
<span class="key">"voteAverage"</span>: <span class="number">7.9</span>
|
||
}
|
||
]
|
||
}
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/tmdb-scraper/confirm</span>
|
||
<span class="api-desc">确认刮削(用户选择TMDB结果后执行)</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>用户从搜索结果中选择一部电影后,调用此接口执行刮削。刮削完成后会更新电影的简介、导演、主演、海报等信息。<strong>注意:两次请求需间隔5秒,防止频繁点击。</strong></p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"movieId"</span>: <span class="number">23</span>,
|
||
<span class="key">"tmdbId"</span>: <span class="number">597</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
<tr><td>userId</td><td>Long</td><td>用户ID</td></tr>
|
||
<tr><td>tmdbId</td><td>Long</td><td>TMDB电影ID</td></tr>
|
||
<tr><td>title</td><td>String</td><td>电影标题(来自TMDB)</td></tr>
|
||
<tr><td>originalTitle</td><td>String</td><td>原始标题(外文)</td></tr>
|
||
<tr><td>releaseDate</td><td>String</td><td>上映日期</td></tr>
|
||
<tr><td>posterPath</td><td>String</td><td>TMDB海报路径</td></tr>
|
||
<tr><td>backdropPath</td><td>String</td><td>TMDB背景图路径</td></tr>
|
||
<tr><td>localPosterUrl</td><td>String</td><td>本地竖版海报URL(MinIO)</td></tr>
|
||
<tr><td>localBackdropUrl</td><td>String</td><td>本地横版背景图URL(MinIO)</td></tr>
|
||
<tr><td>tmdbRating</td><td>Integer</td><td>TMDB评分(0-100分制)</td></tr>
|
||
<tr><td>voteCount</td><td>Integer</td><td>投票数</td></tr>
|
||
<tr><td>runtime</td><td>Integer</td><td>片长(分钟)</td></tr>
|
||
<tr><td>genres</td><td>String</td><td>类型(逗号分隔)</td></tr>
|
||
<tr><td>directors</td><td>String</td><td>导演(逗号分隔,最多2位)</td></tr>
|
||
<tr><td>casts</td><td>String</td><td>主演(逗号分隔,最多5位)</td></tr>
|
||
<tr><td>originalLanguage</td><td>String</td><td>原始语言</td></tr>
|
||
<tr><td>productionCountries</td><td>String</td><td>制片国家(逗号分隔)</td></tr>
|
||
<tr><td>cacheTime</td><td>String</td><td>缓存时间</td></tr>
|
||
<tr><td>status</td><td>String</td><td>缓存状态</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"刮削成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"movieId"</span>: <span class="number">23</span>,
|
||
<span class="key">"userId"</span>: <span class="number">1</span>,
|
||
<span class="key">"tmdbId"</span>: <span class="number">597</span>,
|
||
<span class="key">"title"</span>: <span class="string">"泰坦尼克号"</span>,
|
||
<span class="key">"originalTitle"</span>: <span class="string">"Titanic"</span>,
|
||
<span class="key">"releaseDate"</span>: <span class="string">"1997-11-18"</span>,
|
||
<span class="key">"posterPath"</span>: <span class="string">"/9xjZS2rlVxm8SFx8kPC3JIGCOY.jpg"</span>,
|
||
<span class="key">"backdropPath"</span>: <span class="string">"/rTh4K5uw9HypmpGslcKd4QfHl93.jpg"</span>,
|
||
<span class="key">"localPosterUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/poster_23_597.jpg"</span>,
|
||
<span class="key">"localBackdropUrl"</span>: <span class="string">"http://...:39000/wangpan/posters/backdrop_23_597.jpg"</span>,
|
||
<span class="key">"tmdbRating"</span>: <span class="number">78</span>,
|
||
<span class="key">"voteCount"</span>: <span class="number">24563</span>,
|
||
<span class="key">"runtime"</span>: <span class="number">194</span>,
|
||
<span class="key">"genres"</span>: <span class="string">"剧情, 爱情, 灾难"</span>,
|
||
<span class="key">"directors"</span>: <span class="string">"James Cameron"</span>,
|
||
<span class="key">"casts"</span>: <span class="string">"Leonardo DiCaprio, Kate Winslet, Billy Zane, Kathy Bates, Frances Fisher"</span>,
|
||
<span class="key">"originalLanguage"</span>: <span class="string">"en"</span>,
|
||
<span class="key">"productionCountries"</span>: <span class="string">"United States of America"</span>,
|
||
<span class="key">"cacheTime"</span>: <span class="string">"2026-06-26T16:30:00"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 操作过于频繁</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"操作过于频繁,请等待 3 秒后再试"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 电影不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"电影不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 未登录或 Token 无效</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">401</span>,
|
||
<span class="key">"message"</span>: <span class="string">"未登录或 Token 已过期"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/tmdb-scraper/batch-scrape</span>
|
||
<span class="api-desc">批量刮削</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求体(movieIds 可选)</h4>
|
||
<div class="code-block">{ <span class="key">"movieIds"</span>: [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] } <span style="color:#718096">// 可选:不传则自动查找需要刮削的电影</span></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"批量刮削完成"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"successCount"</span>: <span class="number">2</span>,
|
||
<span class="key">"failCount"</span>: <span class="number">0</span>,
|
||
<span class="key">"total"</span>: <span class="number">2</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/tmdb-scraper/caches</span>
|
||
<span class="api-desc">查询缓存列表</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>默认值</th><th>说明</th></tr>
|
||
<tr><td>pageNo</td><td>1</td><td>页码</td></tr>
|
||
<tr><td>pageSize</td><td>20</td><td>每页条数</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/tmdb-scraper/stats</span>
|
||
<span class="api-desc">刮削统计</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"totalCaches"</span>: <span class="number">5</span>,
|
||
<span class="key">"activeCaches"</span>: <span class="number">3</span>,
|
||
<span class="key">"expiredCaches"</span>: <span class="number">2</span>,
|
||
<span class="key">"userId"</span>: <span class="number">2</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/tmdb-scraper/trigger</span>
|
||
<span class="api-desc">手动触发定时任务</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>手动触发 TMDB 定时扫描任务,查找需要刮削的电影并执行刮削。无需请求体。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"扫描任务已触发"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"message"</span>: <span class="string">"扫描任务已触发"</span>,
|
||
<span class="key">"triggeredBy"</span>: <span class="number">2</span>,
|
||
<span class="key">"triggerType"</span>: <span class="string">"manual"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/tmdb-scraper/scraper-logs</span>
|
||
<span class="api-desc">查询刮削日志(分页)</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>分页查询定时任务执行记录,返回每条记录的汇总信息和详细结果(包含每部电影的刮削结果和失败原因)。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>查询参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>pageNo</td><td>Integer</td><td>否</td><td>页码,默认 1</td></tr>
|
||
<tr><td>pageSize</td><td>Integer</td><td>否</td><td>每页条数,默认 20,最大 50</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>total</td><td>Long</td><td>总记录数</td></tr>
|
||
<tr><td>pageNo</td><td>Integer</td><td>当前页码</td></tr>
|
||
<tr><td>pageSize</td><td>Integer</td><td>每页条数</td></tr>
|
||
<tr><td>list[].id</td><td>Long</td><td>日志ID</td></tr>
|
||
<tr><td>list[].trigger_type</td><td>String</td><td>触发方式:auto(定时自动)/ manual(手动触发)</td></tr>
|
||
<tr><td>list[].status</td><td>String</td><td>执行状态:success / partial / failed / skipped</td></tr>
|
||
<tr><td>list[].total_processed</td><td>Integer</td><td>总处理电影数</td></tr>
|
||
<tr><td>list[].total_success</td><td>Integer</td><td>成功刮削数</td></tr>
|
||
<tr><td>list[].total_skipped</td><td>Integer</td><td>跳过数</td></tr>
|
||
<tr><td>list[].total_failed</td><td>Integer</td><td>失败数</td></tr>
|
||
<tr><td>list[].user_count</td><td>Integer</td><td>涉及用户数</td></tr>
|
||
<tr><td>list[].duration_ms</td><td>Long</td><td>执行耗时(毫秒)</td></tr>
|
||
<tr><td>list[].start_time</td><td>String</td><td>开始时间</td></tr>
|
||
<tr><td>list[].end_time</td><td>String</td><td>结束时间</td></tr>
|
||
<tr><td>list[].error_message</td><td>String</td><td>错误信息(如有)</td></tr>
|
||
<tr><td>list[].details</td><td>Array</td><td>详细执行结果(解析后的JSON)</td></tr>
|
||
<tr><td>list[].details[].username</td><td>String</td><td>用户名</td></tr>
|
||
<tr><td>list[].details[].userId</td><td>Long</td><td>用户ID</td></tr>
|
||
<tr><td>list[].details[].processed</td><td>Integer</td><td>该用户处理的电影数</td></tr>
|
||
<tr><td>list[].details[].success</td><td>Integer</td><td>该用户成功刮削数</td></tr>
|
||
<tr><td>list[].details[].skipped</td><td>Integer</td><td>该用户跳过数</td></tr>
|
||
<tr><td>list[].details[].failed</td><td>Integer</td><td>该用户失败数</td></tr>
|
||
<tr><td>list[].details[].messages</td><td>Array</td><td>失败详情列表(格式:电影名称: 失败原因)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"total"</span>: <span class="number">10</span>,
|
||
<span class="key">"pageNo"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">20</span>,
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"trigger_type"</span>: <span class="string">"auto"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"partial"</span>,
|
||
<span class="key">"total_processed"</span>: <span class="number">5</span>,
|
||
<span class="key">"total_success"</span>: <span class="number">4</span>,
|
||
<span class="key">"total_skipped"</span>: <span class="number">0</span>,
|
||
<span class="key">"total_failed"</span>: <span class="number">1</span>,
|
||
<span class="key">"user_count"</span>: <span class="number">1</span>,
|
||
<span class="key">"duration_ms"</span>: <span class="number">12500</span>,
|
||
<span class="key">"start_time"</span>: <span class="string">"2026-06-27T10:00:00"</span>,
|
||
<span class="key">"end_time"</span>: <span class="string">"2026-06-27T10:00:12"</span>,
|
||
<span class="key">"error_message"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"details"</span>: [
|
||
{
|
||
<span class="key">"username"</span>: <span class="string">"testuser"</span>,
|
||
<span class="key">"userId"</span>: <span class="number">1</span>,
|
||
<span class="key">"processed"</span>: <span class="number">5</span>,
|
||
<span class="key">"success"</span>: <span class="number">4</span>,
|
||
<span class="key">"skipped"</span>: <span class="number">0</span>,
|
||
<span class="key">"failed"</span>: <span class="number">1</span>,
|
||
<span class="key">"messages"</span>: [
|
||
<span class="string">"泰坦尼克号: TMDB未找到匹配的电影"</span>
|
||
]
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/tmdb-scraper/scraper-logs/{id}</span>
|
||
<span class="api-desc">查询单条刮削日志详情</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>查询单条刮削日志的详细信息,包括每个用户的刮削结果、失败电影列表及原因。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>日志ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>id</td><td>Long</td><td>日志ID</td></tr>
|
||
<tr><td>trigger_type</td><td>String</td><td>触发方式:auto(定时自动)/ manual(手动触发)</td></tr>
|
||
<tr><td>status</td><td>String</td><td>执行状态:success / partial / failed / skipped</td></tr>
|
||
<tr><td>total_processed</td><td>Integer</td><td>总处理电影数</td></tr>
|
||
<tr><td>total_success</td><td>Integer</td><td>成功刮削数</td></tr>
|
||
<tr><td>total_skipped</td><td>Integer</td><td>跳过数</td></tr>
|
||
<tr><td>total_failed</td><td>Integer</td><td>失败数</td></tr>
|
||
<tr><td>user_count</td><td>Integer</td><td>涉及用户数</td></tr>
|
||
<tr><td>duration_ms</td><td>Long</td><td>执行耗时(毫秒)</td></tr>
|
||
<tr><td>start_time</td><td>String</td><td>开始时间</td></tr>
|
||
<tr><td>end_time</td><td>String</td><td>结束时间</td></tr>
|
||
<tr><td>error_message</td><td>String</td><td>错误信息(如有)</td></tr>
|
||
<tr><td>details</td><td>Array</td><td>详细执行结果(解析后的JSON)</td></tr>
|
||
<tr><td>details[].username</td><td>String</td><td>用户名</td></tr>
|
||
<tr><td>details[].userId</td><td>Long</td><td>用户ID</td></tr>
|
||
<tr><td>details[].processed</td><td>Integer</td><td>该用户处理的电影数</td></tr>
|
||
<tr><td>details[].success</td><td>Integer</td><td>该用户成功刮削数</td></tr>
|
||
<tr><td>details[].skipped</td><td>Integer</td><td>该用户跳过数</td></tr>
|
||
<tr><td>details[].failed</td><td>Integer</td><td>该用户失败数</td></tr>
|
||
<tr><td>details[].messages</td><td>Array</td><td>失败详情列表(格式:电影名称: 失败原因)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"查询成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"trigger_type"</span>: <span class="string">"auto"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"partial"</span>,
|
||
<span class="key">"total_processed"</span>: <span class="number">5</span>,
|
||
<span class="key">"total_success"</span>: <span class="number">4</span>,
|
||
<span class="key">"total_skipped"</span>: <span class="number">0</span>,
|
||
<span class="key">"total_failed"</span>: <span class="number">1</span>,
|
||
<span class="key">"user_count"</span>: <span class="number">1</span>,
|
||
<span class="key">"duration_ms"</span>: <span class="number">12500</span>,
|
||
<span class="key">"start_time"</span>: <span class="string">"2026-06-27T10:00:00"</span>,
|
||
<span class="key">"end_time"</span>: <span class="string">"2026-06-27T10:00:12"</span>,
|
||
<span class="key">"error_message"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"details"</span>: [
|
||
{
|
||
<span class="key">"username"</span>: <span class="string">"testuser"</span>,
|
||
<span class="key">"userId"</span>: <span class="number">1</span>,
|
||
<span class="key">"processed"</span>: <span class="number">5</span>,
|
||
<span class="key">"success"</span>: <span class="number">4</span>,
|
||
<span class="key">"skipped"</span>: <span class="number">0</span>,
|
||
<span class="key">"failed"</span>: <span class="number">1</span>,
|
||
<span class="key">"messages"</span>: [
|
||
<span class="string">"泰坦尼克号: TMDB未找到匹配的电影"</span>,
|
||
<span class="string">"阿凡达: TMDB API请求失败,请检查API Key是否有效"</span>
|
||
]
|
||
}
|
||
]
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例 — 日志不存在</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">404</span>,
|
||
<span class="key">"message"</span>: <span class="string">"日志不存在"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/tmdb-scraper/cache/{movieId}</span>
|
||
<span class="api-desc">删除缓存</span><span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p>删除指定电影的 TMDB 刮削缓存,下次刮削时将重新从 TMDB 获取数据。</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"缓存删除成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- OpenList 集成 -->
|
||
<div class="section" id="openlist">
|
||
<div class="service-card">
|
||
<h2>OpenList 用户级连接管理</h2>
|
||
<span class="port">wangpan-drive-service : 8082 · OpenListController / DriveController</span>
|
||
</div>
|
||
|
||
<div class="service-card" style="border-left-color: #48bb78;">
|
||
<h2>功能说明</h2>
|
||
<p style="font-size: 14px; color: #4a5568; line-height: 1.8; margin-top: 8px;">
|
||
每个用户可以绑定自己的 OpenList 实例(地址、账号、密码),互不干扰。
|
||
敏感信息使用 Jasypt 加密存储,Token 缓存到数据库避免重复登录。
|
||
OpenList 负责文件操作(同步、播放),TMDB 刮削使用当前用户的 API Key。
|
||
</p>
|
||
<div class="arch-diagram" style="margin-top: 16px;">
|
||
<div class="arch-flow">
|
||
<div class="arch-box" style="background: #4a5568;">前端</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box gateway">Gateway:8888</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box drive">Drive:8082</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box" style="background: #38b2ac;">用户A的OpenList</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box" style="background: #e53e3e;">UC网盘</div>
|
||
</div>
|
||
<div class="arch-flow" style="margin-top: 8px;">
|
||
<div class="arch-box" style="background: #4a5568; visibility: hidden;">前端</div>
|
||
<span class="arch-arrow" style="visibility: hidden;">→</span>
|
||
<div class="arch-box gateway" style="visibility: hidden;">Gateway</div>
|
||
<span class="arch-arrow" style="visibility: hidden;">→</span>
|
||
<div class="arch-box drive" style="visibility: hidden;">Drive</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box" style="background: #38b2ac;">用户B的OpenList</div>
|
||
<span class="arch-arrow">→</span>
|
||
<div class="arch-box" style="background: #e53e3e;">阿里云盘</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="service-card" style="border-left-color: #ed8936;">
|
||
<h2>使用流程</h2>
|
||
<p style="font-size: 14px; color: #4a5568; line-height: 1.8; margin-top: 8px;">
|
||
1. 前端调用 <code>POST /api/openlist/test</code> 测试 OpenList 连接(可选)<br>
|
||
2. 前端调用 <code>POST /api/openlist/bind</code> 绑定 OpenList(传入地址、账号、密码)<br>
|
||
3. 绑定成功后后端自动异步同步:递归遍历 OpenList 目录树,同步文件夹层级和视频文件到数据库<br>
|
||
4. 也可手动调用 <code>POST /api/openlist/sync</code> 触发全量同步<br>
|
||
5. 同步后电影可进行 TMDB 刮削,播放视频自动使用用户绑定的 OpenList 连接<br>
|
||
6. 后端每 24 小时自动检测 OpenList 心跳,记录连接状态<br>
|
||
7. TMDB 刮削使用当前用户的 API Key(与 OpenList 连接隔离)
|
||
</p>
|
||
</div>
|
||
|
||
<!-- ==================== OpenList 连接管理接口 ==================== -->
|
||
|
||
<!-- 测试连接 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/openlist/test</span>
|
||
<span class="api-desc">测试 OpenList 连接(不保存)</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token>
|
||
Content-Type: application/json</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"openlistUrl"</span>: <span class="string">"http://www.example.com:3000"</span>,
|
||
<span class="key">"openlistUsername"</span>: <span class="string">"用户名"</span>,
|
||
<span class="key">"openlistPassword"</span>: <span class="string">"密码"</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>openlistUrl</td><td>String</td><td>是</td><td>OpenList 服务地址(如 http://example.com:13000)</td></tr>
|
||
<tr><td>openlistUsername</td><td>String</td><td>是</td><td>OpenList 账号</td></tr>
|
||
<tr><td>openlistPassword</td><td>String</td><td>是</td><td>OpenList 密码</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"连接成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"连接失败:Invalid username or password"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 绑定 OpenList -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/openlist/bind</span>
|
||
<span class="api-desc">绑定 OpenList 连接</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token>
|
||
Content-Type: application/json</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>请求体</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"openlistUrl"</span>: <span class="string">"http://example.com:13000"</span>,
|
||
<span class="key">"openlistUsername"</span>: <span class="string">"your_username"</span>,
|
||
<span class="key">"openlistPassword"</span>: <span class="string">"your_password"</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>必填</th><th>说明</th></tr>
|
||
<tr><td>openlistUrl</td><td>String</td><td>是</td><td>OpenList 服务地址</td></tr>
|
||
<tr><td>openlistUsername</td><td>String</td><td>是</td><td>OpenList 账号</td></tr>
|
||
<tr><td>openlistPassword</td><td>String</td><td>是</td><td>OpenList 密码</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p style="font-size: 13px; color: #4a5568; line-height: 1.6;">
|
||
验证连接 → 保存配置(敏感信息 Jasypt 加密) → 缓存 Token(24小时有效期)。<br>
|
||
每个用户只能绑定一个 OpenList 实例,重复绑定会更新配置。<br>
|
||
<strong>绑定成功后自动触发异步同步</strong>:递归遍历 OpenList 目录树,同步文件夹层级和视频文件到数据库。<br>
|
||
同步只处理视频格式文件(mp4, mkv, avi 等),非视频文件自动跳过。
|
||
</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"绑定成功,正在后台同步文件..."</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"绑定失败:连接失败:Invalid username or password"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 解绑 OpenList -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method DELETE">DELETE</span>
|
||
<span class="api-path">/api/openlist/unbind</span>
|
||
<span class="api-desc">解绑 OpenList 连接</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p style="font-size: 13px; color: #4a5568; line-height: 1.6;">
|
||
删除当前用户的 OpenList 连接配置(包括加密的 URL、账号、密码和 Token 缓存)。<br>
|
||
解绑后同步文件和播放链接接口将不可用。
|
||
</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"解绑成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"解绑失败:未找到绑定记录"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 查询连接信息 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/openlist/info</span>
|
||
<span class="api-desc">查询当前用户 OpenList 连接信息</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p style="font-size: 13px; color: #4a5568; line-height: 1.6;">
|
||
返回脱敏后的连接信息(URL、用户名均脱敏显示,不返回明文密码)。<br>
|
||
会实际调用 OpenList 登录接口验证当前连接是否可用,返回 <code>connected</code> 和 <code>connectMessage</code> 字段。<br>
|
||
未绑定时返回 <code>null</code>。
|
||
</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例(已绑定)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"操作成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"openlistUrl"</span>: <span class="string">"http://ld***:13000"</span>,
|
||
<span class="key">"openlistUsername"</span>: <span class="string">"dj***22"</span>,
|
||
<span class="key">"storageId"</span>: <span class="string">"1"</span>,
|
||
<span class="key">"mountPath"</span>: <span class="string">"/uc"</span>,
|
||
<span class="key">"status"</span>: <span class="string">"active"</span>,
|
||
<span class="key">"tokenValid"</span>: <span class="keyword">true</span>,
|
||
<span class="key">"connected"</span>: <span class="keyword">true</span>,
|
||
<span class="key">"connectMessage"</span>: <span class="string">"连接成功"</span>,
|
||
<span class="key">"lastHeartbeatTime"</span>: <span class="string">"2026-06-27T15:00:00"</span>,
|
||
<span class="key">"heartbeatStatus"</span>: <span class="string">"success"</span>,
|
||
<span class="key">"heartbeatError"</span>: <span class="keyword">null</span>,
|
||
<span class="key">"createTime"</span>: <span class="string">"2026-06-26T15:00:00"</span>,
|
||
<span class="key">"updateTime"</span>: <span class="string">"2026-06-26T15:00:00"</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>说明</th></tr>
|
||
<tr><td>openlistUrl</td><td>OpenList 服务地址(脱敏显示,如 http://ld***:13000)</td></tr>
|
||
<tr><td>openlistUsername</td><td>OpenList 账号(脱敏显示,如 dj***22)</td></tr>
|
||
<tr><td>storageId</td><td>OpenList 存储 ID(用户在 OpenList 后台配置)</td></tr>
|
||
<tr><td>mountPath</td><td>挂载路径(如 /uc)</td></tr>
|
||
<tr><td>status</td><td>状态:active / disabled</td></tr>
|
||
<tr><td>tokenValid</td><td>本地缓存的 Token 是否有效(true 表示未过期)</td></tr>
|
||
<tr><td>connected</td><td>实时连接测试结果:true 表示当前账号密码可成功连接 OpenList</td></tr>
|
||
<tr><td>connectMessage</td><td>实时连接测试结果描述,如 "连接成功" 或 "连接失败:Invalid username or password"</td></tr>
|
||
<tr><td>lastHeartbeatTime</td><td>最后一次定时心跳检测时间(每 24 小时自动检测一次)</td></tr>
|
||
<tr><td>heartbeatStatus</td><td>最近一次心跳检测结果:success / failed</td></tr>
|
||
<tr><td>heartbeatError</td><td>心跳检测失败原因(成功时为 null)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例(未绑定)</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"操作成功"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ==================== OpenList 文件同步接口 ==================== -->
|
||
|
||
<!-- 手动同步文件 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/openlist/sync</span>
|
||
<span class="api-desc">手动触发 OpenList 文件同步</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p style="font-size: 13px; color: #4a5568; line-height: 1.6;">
|
||
从 OpenList 根目录开始递归遍历目录树,同步文件夹层级和视频文件到本地数据库。<br>
|
||
<strong>同步策略</strong>:<br>
|
||
1. 自动为用户创建/复用关联 OpenList 的网盘(Drive)<br>
|
||
2. 递归遍历 OpenList 目录树,在本地创建对应的文件夹(DriveFolder)<br>
|
||
3. <strong>只同步视频格式文件</strong>(mp4, mkv, avi, mov, wmv, flv, webm 等),非视频文件自动跳过<br>
|
||
4. 视频文件创建为电影记录(DriveMovie),<code>storageType=openlist</code><br>
|
||
5. 不含视频文件的空文件夹也会被跳过<br>
|
||
<strong>前置条件</strong>:用户已通过 <code>/api/openlist/bind</code> 绑定 OpenList 连接。<br>
|
||
<strong>注意</strong>:绑定 OpenList 时已自动触发异步同步,此接口用于手动重新同步。
|
||
</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"同步完成"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"driveCount"</span>: <span class="number">1</span>,
|
||
<span class="key">"folderCount"</span>: <span class="number">10</span>,
|
||
<span class="key">"movieCount"</span>: <span class="number">50</span>,
|
||
<span class="key">"skippedFileCount"</span>: <span class="number">5</span>,
|
||
<span class="key">"skippedFolderCount"</span>: <span class="number">2</span>,
|
||
<span class="key">"errorCount"</span>: <span class="number">0</span>,
|
||
<span class="key">"errors"</span>: []
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveCount</td><td>int</td><td>关联网盘数量(通常为 1)</td></tr>
|
||
<tr><td>folderCount</td><td>int</td><td>本次同步创建的文件夹数量</td></tr>
|
||
<tr><td>movieCount</td><td>int</td><td>本次同步创建的视频文件(电影)数量</td></tr>
|
||
<tr><td>skippedFileCount</td><td>int</td><td>跳过的非视频文件数量</td></tr>
|
||
<tr><td>skippedFolderCount</td><td>int</td><td>跳过的不含视频的文件夹数量</td></tr>
|
||
<tr><td>errorCount</td><td>int</td><td>同步过程中的错误数量</td></tr>
|
||
<tr><td>errors</td><td>Array</td><td>错误详情列表(字符串数组)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"同步失败:用户未绑定 OpenList,请先绑定"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ==================== 网盘/电影接口(使用用户级 OpenList 连接) ==================== -->
|
||
|
||
<!-- 同步文件夹文件 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method POST">POST</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders/{folderId}/sync-files</span>
|
||
<span class="api-desc">从 OpenList 同步文件夹文件</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘 ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>文件夹 ID(0 表示根目录)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p style="font-size: 13px; color: #4a5568; line-height: 1.6;">
|
||
使用当前用户绑定的 OpenList 连接,获取指定路径下的文件列表,自动创建数据库中缺失的电影记录。<br>
|
||
已存在的文件不会重复创建。同步后文件可进行 TMDB 刮削。<br>
|
||
<strong>前置条件</strong>:用户已通过 <code>/api/openlist/bind</code> 绑定 OpenList 连接。
|
||
</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"同步成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"syncCount"</span>: <span class="number">5</span>,
|
||
<span class="key">"totalFiles"</span>: <span class="number">10</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>说明</th></tr>
|
||
<tr><td>syncCount</td><td>本次新增的电影记录数量</td></tr>
|
||
<tr><td>totalFiles</td><td>OpenList 中该路径下的文件总数</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"同步失败:用户未绑定 OpenList,请先在设置中绑定 OpenList 连接"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 获取电影播放链接 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/{driveId}/movies/{movieId}/play-url</span>
|
||
<span class="api-desc">获取电影真实播放链接</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘 ID</td></tr>
|
||
<tr><td>movieId</td><td>Long</td><td>电影 ID</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>功能说明</h4>
|
||
<p style="font-size: 13px; color: #4a5568; line-height: 1.6;">
|
||
使用当前用户绑定的 OpenList 连接,获取文件的真实下载地址,返回的 URL 可直接用于视频播放器播放。<br>
|
||
仅支持 <code>storageType</code> 为 <code>openlist</code> 的电影记录。<br>
|
||
Token 过期时自动刷新,对用户透明。
|
||
</p>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"获取成功"</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"playUrl"</span>: <span class="string">"https://uc网盘真实下载地址/xxx.mp4"</span>,
|
||
<span class="key">"fileName"</span>: <span class="string">"阿凡达.mp4"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">1073741824</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>响应字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>说明</th></tr>
|
||
<tr><td>playUrl</td><td>真实播放/下载地址,可直接用于视频播放器</td></tr>
|
||
<tr><td>fileName</td><td>文件名</td></tr>
|
||
<tr><td>fileSize</td><td>文件大小(字节)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>失败响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">500</span>,
|
||
<span class="key">"message"</span>: <span class="string">"获取失败:该电影不是 OpenList 管理的文件"</span>,
|
||
<span class="key">"data"</span>: <span class="keyword">null</span>
|
||
}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 查看电影列表 -->
|
||
<div class="api-item">
|
||
<div class="api-header" onclick="toggleDetail(this)">
|
||
<span class="method GET">GET</span>
|
||
<span class="api-path">/api/drive/{driveId}/folders/{folderId}/movies</span>
|
||
<span class="api-desc">查看电影列表(含 OpenList 信息)</span>
|
||
<span class="auth-badge">需Token</span>
|
||
</div>
|
||
<div class="api-detail">
|
||
<div class="detail-section">
|
||
<h4>请求头</h4>
|
||
<div class="code-block">Authorization: Bearer <token></div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>路径参数</h4>
|
||
<table class="param-table">
|
||
<tr><th>参数</th><th>类型</th><th>说明</th></tr>
|
||
<tr><td>driveId</td><td>Long</td><td>网盘 ID</td></tr>
|
||
<tr><td>folderId</td><td>Long</td><td>文件夹 ID(0 表示根目录)</td></tr>
|
||
</table>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>成功响应示例</h4>
|
||
<div class="code-block">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"data"</span>: {
|
||
<span class="key">"list"</span>: [
|
||
{
|
||
<span class="key">"id"</span>: <span class="number">1</span>,
|
||
<span class="key">"title"</span>: <span class="string">"阿凡达.mp4"</span>,
|
||
<span class="key">"openlistFilePath"</span>: <span class="string">"/uc/阿凡达.mp4"</span>,
|
||
<span class="key">"storageType"</span>: <span class="string">"openlist"</span>,
|
||
<span class="key">"fileSize"</span>: <span class="number">1073741824</span>,
|
||
<span class="key">"mimeType"</span>: <span class="string">"video/mp4"</span>
|
||
}
|
||
],
|
||
<span class="key">"total"</span>: <span class="number">10</span>,
|
||
<span class="key">"page"</span>: <span class="number">1</span>,
|
||
<span class="key">"pageSize"</span>: <span class="number">20</span>
|
||
}
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>OpenList 相关字段说明</h4>
|
||
<table class="param-table">
|
||
<tr><th>字段</th><th>说明</th></tr>
|
||
<tr><td>openlistFilePath</td><td>OpenList 文件路径(如 "/uc/阿凡达.mp4"),由系统根据用户绑定的挂载路径自动生成</td></tr>
|
||
<tr><td>storageType</td><td>存储类型:<code>metadata</code>(仅元数据)/ <code>openlist</code>(OpenList 管理)</td></tr>
|
||
<tr><td>fileSize</td><td>文件大小(字节),同步时从 OpenList 获取</td></tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 前端调用示例 -->
|
||
<div class="service-card" style="border-left-color: #9f7aea;">
|
||
<h2>前端调用示例</h2>
|
||
<div class="detail-section">
|
||
<h4>1. 绑定 OpenList</h4>
|
||
<div class="code-block">// 先测试连接
|
||
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'
|
||
})
|
||
});</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>2. 查询连接信息</h4>
|
||
<div class="code-block">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');
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>3. 手动同步文件</h4>
|
||
<div class="code-block">// 绑定后已自动触发异步同步,也可手动重新同步
|
||
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);
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>4. 查询连接状态</h4>
|
||
<div class="code-block">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');
|
||
}</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>5. 播放视频</h4>
|
||
<div class="code-block">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();</div>
|
||
</div>
|
||
<div class="detail-section">
|
||
<h4>6. 解绑 OpenList</h4>
|
||
<div class="code-block">const resp = await fetch('/api/openlist/unbind', {
|
||
method: 'DELETE',
|
||
headers: { 'Authorization': `Bearer ${token}` }
|
||
});
|
||
const data = await resp.json();
|
||
console.log(data.message); // "解绑成功"</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 状态码 -->
|
||
<div class="section" id="status">
|
||
<div class="service-card">
|
||
<h2>通用响应格式</h2>
|
||
<div class="code-block" style="margin-top: 12px;">{
|
||
<span class="key">"code"</span>: <span class="number">200</span>,
|
||
<span class="key">"message"</span>: <span class="string">"操作结果描述"</span>,
|
||
<span class="key">"data"</span>: { <span class="string">...</span> }
|
||
}</div>
|
||
</div>
|
||
|
||
<div class="service-card">
|
||
<h2>状态码说明</h2>
|
||
<table class="status-table">
|
||
<tr><th>状态码</th><th>说明</th></tr>
|
||
<tr><td>200</td><td>请求成功</td></tr>
|
||
<tr><td>400</td><td>请求参数错误</td></tr>
|
||
<tr><td>401</td><td>未授权 / 令牌无效</td></tr>
|
||
<tr><td>403</td><td>权限不足</td></tr>
|
||
<tr><td>404</td><td>资源不存在</td></tr>
|
||
<tr><td>500</td><td>服务器内部错误</td></tr>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="service-card">
|
||
<h2>认证说明</h2>
|
||
<p style="font-size: 14px; color: #4a5568; line-height: 1.8;">
|
||
除以下 <strong>3 个接口</strong> 外,所有接口都需要在请求头中携带访问令牌:<br><br>
|
||
|
||
<strong>无需 Token 的接口:</strong><br>
|
||
<code style="background:#c6f6d5;padding:2px 8px;border-radius:4px;">POST /api/login</code> 用户登录<br>
|
||
<code style="background:#c6f6d5;padding:2px 8px;border-radius:4px;">POST /api/register</code> 用户注册<br>
|
||
<code style="background:#c6f6d5;padding:2px 8px;border-radius:4px;">GET /api/token/validate?token=xxx</code> 令牌验证(token 通过 URL 参数传递)<br><br>
|
||
|
||
<strong>需要 Token 的接口(带 <span style="background:#ed8936;color:white;padding:1px 6px;border-radius:10px;font-size:10px;font-weight:600;">需Token</span> 标识):</strong><br>
|
||
在请求头中添加:<code style="background: #edf2f7; padding: 2px 8px; border-radius: 4px;">Authorization: Bearer <token></code>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 导航切换
|
||
document.querySelectorAll('.nav-item').forEach(item => {
|
||
item.addEventListener('click', function() {
|
||
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
|
||
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
|
||
this.classList.add('active');
|
||
const sectionId = this.dataset.section;
|
||
const section = document.getElementById(sectionId);
|
||
if (section) {
|
||
section.classList.add('active');
|
||
} else {
|
||
console.error('Section not found:', sectionId);
|
||
}
|
||
});
|
||
});
|
||
|
||
// 页面加载时默认激活第一个导航项
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const firstNavItem = document.querySelector('.nav-item');
|
||
if (firstNavItem && !document.querySelector('.section.active')) {
|
||
firstNavItem.click();
|
||
}
|
||
});
|
||
|
||
// API 详情展开/收起
|
||
function toggleDetail(header) {
|
||
const detail = header.nextElementSibling;
|
||
detail.classList.toggle('show');
|
||
}
|
||
|
||
// 搜索功能
|
||
const searchInput = document.getElementById('apiSearch');
|
||
const searchClear = document.getElementById('searchClear');
|
||
const searchResultCount = document.getElementById('searchResultCount');
|
||
let searchTimeout;
|
||
|
||
searchInput.addEventListener('input', function() {
|
||
clearTimeout(searchTimeout);
|
||
searchTimeout = setTimeout(() => performSearch(this.value), 200);
|
||
|
||
// 显示/隐藏清除按钮
|
||
searchClear.classList.toggle('show', this.value.length > 0);
|
||
});
|
||
|
||
searchClear.addEventListener('click', function() {
|
||
searchInput.value = '';
|
||
performSearch('');
|
||
searchClear.classList.remove('show');
|
||
searchInput.focus();
|
||
});
|
||
|
||
// 支持快捷键 Ctrl/Cmd + K 聚焦搜索框
|
||
document.addEventListener('keydown', function(e) {
|
||
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||
e.preventDefault();
|
||
searchInput.focus();
|
||
searchInput.select();
|
||
}
|
||
// ESC 键清除搜索
|
||
if (e.key === 'Escape' && document.activeElement === searchInput) {
|
||
searchInput.value = '';
|
||
performSearch('');
|
||
searchClear.classList.remove('show');
|
||
}
|
||
});
|
||
|
||
function performSearch(keyword) {
|
||
const apiItems = document.querySelectorAll('.api-item');
|
||
const sections = document.querySelectorAll('.section');
|
||
|
||
// 清除之前的高亮
|
||
document.querySelectorAll('.highlight').forEach(el => {
|
||
const parent = el.parentNode;
|
||
parent.replaceChild(document.createTextNode(el.textContent), el);
|
||
parent.normalize();
|
||
});
|
||
|
||
if (!keyword.trim()) {
|
||
// 恢复所有项目
|
||
apiItems.forEach(item => item.classList.remove('search-hidden'));
|
||
searchResultCount.classList.remove('show');
|
||
|
||
// 恢复导航栏
|
||
document.querySelectorAll('.nav-item').forEach(item => {
|
||
item.style.display = '';
|
||
});
|
||
|
||
// 恢复 section 显示状态
|
||
sections.forEach(section => {
|
||
section.style.display = '';
|
||
});
|
||
return;
|
||
}
|
||
|
||
const lowerKeyword = keyword.toLowerCase();
|
||
let matchCount = 0;
|
||
const matchedSections = new Set();
|
||
|
||
// 全局搜索:遍历所有分类中的所有接口
|
||
apiItems.forEach(item => {
|
||
const path = item.querySelector('.api-path');
|
||
const desc = item.querySelector('.api-desc');
|
||
const method = item.querySelector('.method');
|
||
|
||
if (!path || !desc) return;
|
||
|
||
const pathText = path.textContent.toLowerCase();
|
||
const descText = desc.textContent.toLowerCase();
|
||
const methodText = method ? method.textContent.toLowerCase() : '';
|
||
|
||
const isMatch = pathText.includes(lowerKeyword) ||
|
||
descText.includes(lowerKeyword) ||
|
||
methodText.includes(lowerKeyword);
|
||
|
||
if (isMatch) {
|
||
item.classList.remove('search-hidden');
|
||
matchCount++;
|
||
|
||
// 记录匹配的 section
|
||
const section = item.closest('.section');
|
||
if (section) {
|
||
matchedSections.add(section.id);
|
||
}
|
||
|
||
// 高亮匹配内容
|
||
highlightText(path, keyword);
|
||
highlightText(desc, keyword);
|
||
|
||
// 自动展开匹配的详情
|
||
const detail = item.querySelector('.api-detail');
|
||
if (detail && !detail.classList.contains('show')) {
|
||
detail.classList.add('show');
|
||
}
|
||
} else {
|
||
item.classList.add('search-hidden');
|
||
}
|
||
});
|
||
|
||
// 全局搜索模式:显示所有有匹配结果的分类
|
||
sections.forEach(section => {
|
||
if (section.id === 'overview' || section.id === 'status') {
|
||
// 架构总览和状态码始终显示
|
||
section.style.display = '';
|
||
} else if (matchedSections.has(section.id)) {
|
||
// 有匹配结果的分类强制显示
|
||
section.style.display = 'block';
|
||
} else {
|
||
// 无匹配结果的分类隐藏
|
||
section.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 更新导航栏显示(只显示有匹配结果的分类)
|
||
document.querySelectorAll('.nav-item').forEach(item => {
|
||
const sectionId = item.dataset.section;
|
||
if (sectionId === 'overview' || sectionId === 'status') {
|
||
item.style.display = '';
|
||
} else if (matchedSections.has(sectionId)) {
|
||
item.style.display = '';
|
||
} else {
|
||
item.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 显示搜索结果
|
||
searchResultCount.textContent = `找到 ${matchCount} 个匹配的接口(跨 ${matchedSections.size} 个分类)`;
|
||
searchResultCount.classList.add('show');
|
||
}
|
||
|
||
function highlightText(element, keyword) {
|
||
const text = element.textContent;
|
||
const lowerText = text.toLowerCase();
|
||
const lowerKeyword = keyword.toLowerCase();
|
||
const index = lowerText.indexOf(lowerKeyword);
|
||
|
||
if (index === -1) return;
|
||
|
||
const before = text.substring(0, index);
|
||
const match = text.substring(index, index + keyword.length);
|
||
const after = text.substring(index + keyword.length);
|
||
|
||
element.innerHTML = '';
|
||
if (before) element.appendChild(document.createTextNode(before));
|
||
|
||
const highlight = document.createElement('span');
|
||
highlight.className = 'highlight';
|
||
highlight.textContent = match;
|
||
element.appendChild(highlight);
|
||
|
||
if (after) element.appendChild(document.createTextNode(after));
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|