13 KiB
13 KiB
---
百盘搜 - V1.0 需求文档
最后更新时间: 2025年5月6日
1. 项目概述
1.1. 目标
构建一个Web应用程序,主要目标是聚合来自特定外部源的网盘资源搜索结果,提供统一的搜索界面。用户可以根据网盘类型筛选结果,并能为选定的资源触发一个“获取链接”操作,该操作通过后端管理的网盘账号进行文件转存,并生成一个有时效性的临时分享链接供用户使用。
1.2. 目标用户
面向普通互联网用户,无需注册或登录即可使用核心搜索和获取链接功能。
1.3. 初始范围
- 支持网盘: 百度网盘 (baidu), 夸克网盘 (quark), UC网盘 (uc), 迅雷网盘 (xunlei)。搜索结果需能按这些类型筛选。
- 核心功能:
- 关键词搜索。
- 按网盘类型筛选。
- 获取临时分享链接(通过后端转存)。
- 后台: 实现必要的API、缓存、数据库记录和日志。
2. 技术栈
- 前端: Nuxt.js (使用TypeScript)
- 后端: Express.js (使用TypeScript)
- 数据库: Postgres 14 (ORM: Prisma)
- 缓存: Redis
3. 架构概述
- 前端: 负责用户界面展示、接收用户输入、向后端请求数据、存储从后端获取的完整搜索结果列表、在本地实现所有筛选和分页逻辑。
- 后端:
- 提供RESTful API供前端调用。
- 与唯一的外部搜索API (http://192.168.1.99:8008/api/search) 交互。
- 处理和缓存搜索结果。
- 管理系统自有的网盘账号(每种类型一个)。
- 实现核心的“转存与分享”逻辑(DriveAdapter模式)。
- 执行资源清理的后台任务。
- 记录详细的操作日志到数据库。
- Drive Adapters: 后端采用适配器模式,为每种支持的网盘(Baidu, Quark, UC, Xunlei)实现统一接口,封装其特定的API调用逻辑(如转存、生成分享链接、删除文件)。
4. 功能需求 - 前端
4.1. 搜索页面 (/)
- 界面元素:
- 顶部包含一个关键词输入框和搜索按钮。
- 提供按网盘类型(百度、夸克、UC、迅雷)筛选的控件(如按钮组或复选框)。
- 搜索结果列表区域。
- 分页控件。
- 结果展示:
- 搜索结果以列表形式展示。
- 每项结果至少清晰显示:
- 标题/文件名 (displayTitle)
- 来源网盘 (driveType): 可以用文字或小图标表示。
- 发布日期 (publishDate)
- 每项结果旁边提供一个 “获取链接” 按钮。
- 交互逻辑:
- 用户输入关键词并点击搜索,前端调用后端 GET /api/search 接口。
- 前端接收并存储全部搜索结果数据。
- 用户点击网盘类型筛选控件,前端在本地对已存储的数据进行过滤显示。
- 用户使用分页控件,前端在本地计算并显示对应页码的数据。
- 用户点击某项结果的“获取链接”按钮,前端调用后端 POST /api/generate-link 接口,并传递该项对应的 itemId。前端需要处理等待状态,并在获取到链接或错误码后给用户反馈。
5. 功能需求 - 后端 API
你补充的这个细节非常重要!这会直接影响我们后端与外部搜索服务交互的方式。
我将更新需求文档中关于后端 GET /api/search 接口的部分,以包含这个认证流程。
以下是针对需求文档的补充和修改:
在章节 5. 功能需求 - 后端 API,子章节 5.1. GET /api/search 中,处理流程需要更新:
5.1. GET /api/search
- 输入:
keyword(Query Parameter, String, Required) - 外部API认证配置:
- 后端应用需要安全地配置用于登录外部搜索接口 (
http://192.168.1.99:8008/api/user/login) 的username和password。这些凭证绝不能硬编码,应通过环境变量或加密配置文件提供。 - 后端需要一个机制来存储和管理从外部登录接口获取的当前有效的
token(例如,存储在内存变量中,并考虑持久化到Redis以支持多实例部署和重启后恢复,或者每次应用启动时重新登录获取)。
- 后端应用需要安全地配置用于登录外部搜索接口 (
- 处理流程:
- 确保外部API Token有效:
a. 检查当前是否已持有有效的
token。可以通过检查token的过期时间(如果JWT token中包含exp声明)或通过一个简单的状态标记来判断。 b. 如果token不存在、无效或即将过期: i. 调用外部登录接口POST http://192.168.1.99:8008/api/user/login,请求体包含配置好的username和password。 ii. 如果登录成功,获取返回的新token,并更新后端持有的当前token及其过期信息。记录登录成功日志。 iii.如果登录失败,记录严重错误日志(例如,无法连接外部认证服务,凭证错误),并向上游(即我们的前端)返回一个特定的错误码,表明搜索服务暂时不可用。 - 执行搜索(结合缓存逻辑):
a. 构建Redis缓存Key (例如:
search:${keyword})。尝试从Redis获取缓存。若命中,直接返回缓存数据(包含itemId的列表)。 b. 若未命中缓存: i. 调用外部搜索API (携带Token):GET http://192.168.1.99:8008/api/search?keyword={keyword}在请求头中添加Authorization: Bearer <当前有效的token>。 ii. 处理Token失效(重试机制): 如果调用外部搜索API返回401或403错误(表示Token无效或过期): 1. 立即执行上述步骤 1.b (重新登录获取新Token)。 2. 如果成功获取新Token,则重试一次步骤 2.b.i (调用外部搜索API)。 3. 如果重试仍然失败,或重新登录失败,则记录严重错误日志,并向上游返回错误。 iii.处理外部API的响应数据。对于返回的每个资源项: 1. 生成一个全局唯一的itemId(例如: UUID)。 2. 数据提取与格式化: 确保包含driveType(统一格式),publishDate(ISO 8601),displayTitle, 以及后续“获取链接”所需的原始信息(如cloudLinks, 原始messageId)。 3. 将该项的完整处理后信息存入Redis,Key为itemId,TTL设置为 1-2 天。 iv. 记录本次搜索操作(成功或因外部API问题失败)到ActivityLog表(包含关键词、来源IP等)。 v. 如果外部API调用成功,将包含所有处理后结果项(至少含itemId,driveType,publishDate,displayTitle)的完整列表存入Redis,Key为search:${keyword},TTL建议设置为 5 分钟。 vi. 将此完整列表返回给前端(或在发生不可恢复错误时返回错误响应)。
- 确保外部API Token有效:
a. 检查当前是否已持有有效的
- 输出:
[{ itemId, displayTitle, driveType, publishDate, ... }, ...]或错误响应。 - 并发管理 (建议): 当多个并发请求发现token失效时,应有机制避免同时多次调用外部登录接口(例如,使用一个简单的锁或请求队列来管理token的更新过程)。
5.2. POST /api/generate-link
- 输入: JSON Body { "itemId": "..." } (String, Required)
- 处理流程 (同步执行):
- 记录操作开始 (GET_LINK_START) 到 ActivityLog(包含 itemId, IP等)。
- 使用 itemId 从Redis获取资源的完整信息。若获取失败(Key不存在或过期),返回错误码。
- 从资源信息中确定所需的 driveType。
- 从数据库 Account 表中查找该 driveType 对应的唯一管理账号。若找不到或账号状态无效,记录错误并返回错误码。
- 根据 driveType 获取相应的 DriveAdapter 实例。
- 调用 adapter.transferAndShare(sourceLink, sourceLinkType, managedAccount) 方法执行核心逻辑。
- 若成功: a. transferAndShare 方法应返回生成的 tempLink 和用于后续删除的 transferredFileId。 b. 在 TranscodeRecord 表中记录或更新本次操作的状态为 'success',并保存 tempLink, transferredFileId, 完成时间等信息。 c. 记录操作成功 (GET_LINK_SUCCESS) 到 ActivityLog。 d. 向前端返回 { "success": true, "tempLink": "..." }。
- 若失败 (在任何步骤): a. 在 TranscodeRecord 表中记录或更新本次操作的状态为 'failed',并保存错误信息。 b. 记录操作失败 (GET_LINK_FAILURE) 到 ActivityLog,包含返回给前端的错误码和详细的内部错误信息。 c. 向前端返回 { "success": false, "errorCode": "SOME_ERROR_CODE" } (错误码应预定义,不暴露内部细节)。
- 输出: 成功时返回包含临时链接的JSON,失败时返回包含错误码的JSON。
6. 功能需求 - 后台任务
6.1. 资源清理任务
- 触发: 定时执行(例如: 使用 node-cron 或类似库,每5分钟执行一次)。
- 逻辑:
- 查询 TranscodeRecord 表,筛选条件为:status == 'success', cleanupStatus == 'pending', 且 transferEndTime 早于当前时间减去约10分钟。
- 对每个符合条件的记录: a. 获取 transferredFileId, driveType, managedAccountId。 b. 获取对应的 Account 信息和 DriveAdapter 实例。 c. 调用 adapter.delete(transferredFileId, managedAccount) 方法删除网盘中的文件。 d. 若删除成功: 更新 TranscodeRecord 的 cleanupStatus 为 'done'。记录清理成功 (CLEANUP_SUCCESS) 到 ActivityLog。 e. 若删除失败: 更新 TranscodeRecord 的 cleanupStatus 为 'failed'。记录清理失败 (CLEANUP_FAILURE) 到 ActivityLog,包含错误信息。
7. 数据模型 (Database - Postgres/Prisma)
7.1. Account 表 (管理系统自有的网盘账号)
- id: Int @id @default(autoincrement())
- driveType: String @unique (例如: 'baidu', 'quark', 'uc', 'xunlei')
- accountIdentifier: String (用于标识账号, 如用户名)
- credentials: String @db.Text (存储加密后的JSON字符串,包含API Key/Secret, Cookie等)
- status: String @default("active") (例如: 'active', 'inactive', 'error')
- lastChecked: DateTime?
- createdAt: DateTime @default(now())
- updatedAt: DateTime @updatedAt
7.2. TranscodeRecord 表 (跟踪转存和清理状态)
- id: Int @id @default(autoincrement())
- itemId: String @index (关联的资源项ID)
- sourceLink: String @db.Text
- driveType: String
- managedAccountId: Int (外键关联 Account.id)
- status: String @default("pending") ('pending', 'success', 'failed')
- tempLink: String? @db.Text
- transferredFileId: String? (由Adapter返回,用于删除)
- transferStartTime: DateTime @default(now())
- transferEndTime: DateTime?
- errorCode: String?
- errorMessage: String? @db.Text
- cleanupStatus: String @default("pending") ('pending', 'done', 'failed') @index
- createdAt: DateTime @default(now())
- updatedAt: DateTime @updatedAt
- account: Account @relation(fields: [managedAccountId], references: [id])
7.3. ActivityLog 表 (操作审计日志)
- id: BigInt @id @default(autoincrement())
- timestamp: DateTime @default(now())
- operationType: String ('SEARCH', 'GET_LINK_START', 'GET_LINK_SUCCESS', 'GET_LINK_FAILURE', 'CLEANUP_SUCCESS', 'CLEANUP_FAILURE')
- clientIp: String?
- keyword: String?
- itemId: String? @index
- driveType: String?
- managedAccountId: Int? (关联 Account.id)
- generatedLink: String? @db.Text
- errorCode: String?
- errorMessage: String? @db.Text
- durationMs: Int?
- details: String? @db.Text (存储额外JSON格式的上下文信息)
8. 非功能性需求
- 缓存:
- 搜索结果列表缓存 (search:{keyword} -> Item List): Redis, TTL ~5分钟。
- 单个项目详细信息缓存 (itemId -> Full Item Data): Redis, TTL 1-2天。
- 安全:
- Account 表中的 credentials 字段必须加密存储 (例如: 使用Node.js的 crypto 模块进行 AES 加密,密钥需妥善管理)。
- 后端API需实施速率限制 (例如: 使用 express-rate-limit 中间件,限制 IP 频率,特别是 POST /api/generate-link)。
- 记录请求来源IP地址到 ActivityLog。
- 日志:
- 后端应使用成熟的日志库 (如 Winston, pino) 进行结构化日志记录。
- 关键操作、错误均需记录到 ActivityLog 数据库表。
- 错误处理:
- 后端API在发生内部错误时,应返回预定义的、不暴露细节的错误码给前端。
- 详细的错误信息和堆栈应记录在后端日志和 ActivityLog 的 errorMessage 字段中。
- 前端根据错误码显示统一的、用户友好的错误提示。
9. V1.0 版本范围之外
- 管理后台界面及相关API (/api/stats, /api/accounts)。
- 用户注册、登录系统。
- 除“网盘类型”外的其他高级搜索筛选(文件类型、大小、时间等)。
- 搜索结果中的文件列表预览功能。
- 异步处理“获取链接”请求。
- 支持每种网盘类型配置多个管理账号及选择策略。
- 与日志聚合系统(如Loki)的集成。