# WangPan 网盘系统 > 基于 Spring Cloud 微服务架构的个人网盘系统,支持文件管理、电影元数据刮削(TMDB)、用户认证等功能。 --- ## 目录 - [项目简介](#项目简介) - [技术栈](#技术栈) - [模块架构](#模块架构) - [快速开始](#快速开始) - [配置说明](#配置说明) - [API 接口文档](#api-接口文档) - [数据库设计](#数据库设计) - [项目规范](#项目规范) - [部署指南](#部署指南) - [常见问题](#常见问题) --- ## 项目简介 WangPan 是一个功能完整的个人网盘系统,采用微服务架构设计,主要功能包括: - **用户认证**:注册、登录、Token 管理、登录日志 - **网盘管理**:创建网盘、文件夹管理、电影文件上传/管理 - **TMDB 刮削**:自动从 TMDB 获取电影元数据(标题、评分、海报、简介等) - **Icon 管理**:自定义图标上传与管理 - **API 网关**:统一入口,路由转发,认证拦截 --- ## 技术栈 | 技术 | 版本 | 用途 | |------|------|------| | Java | 17 | 开发语言 | | Spring Boot | 3.2.5 | 应用框架 | | Spring Cloud Gateway | 2023.0.1 | API 网关 | | MyBatis-Plus | 3.5.7 | ORM 框架 | | MySQL | 8.0.33 | 关系型数据库 | | MinIO | 8.5.7 | 对象存储(文件/图片) | | Jasypt | 3.0.5 | 配置文件加密 | | Maven | 3.x | 构建工具 | --- ## 模块架构 ``` wangpan/ ├── wangpan-common/ # 公共模块(DO/Mapper/Repository/Entity/Util/Interceptor) ├── wangpan-user-service/ # 用户认证服务(端口 8081) ├── wangpan-drive-service/ # 网盘管理服务(端口 8082) ├── wangpan-tmdb-service/ # TMDB 刮削服务(端口 8083) └── wangpan-gateway/ # API 网关(端口 8888) ``` ### 服务端口与职责 | 服务 | 端口 | 职责 | |------|------|------| | `wangpan-user-service` | 8081 | 用户注册、登录、Token 签发与验证、登录日志 | | `wangpan-drive-service` | 8082 | 网盘 CRUD、文件夹管理、电影管理、Icon 管理、重复检测 | | `wangpan-tmdb-service` | 8083 | TMDB API 调用、电影元数据刮削、刮削缓存、定时扫描 | | `wangpan-gateway` | 8888 | 统一路由入口、CORS 跨域、认证拦截、API 文档页面 | ### 分层架构 ``` Controller → 接收请求、参数校验、调用 Service、返回 Result ↓ Service → 业务逻辑处理、事务管理 ↓ Repository → 数据访问封装、Entity ↔ DO 转换、LambdaQueryWrapper 查询 ↓ Mapper → MyBatis-Plus BaseMapper 接口 + XML 复杂 SQL ↓ DO → 数据库表映射(@TableName 注解) ``` --- ## 快速开始 ### 环境要求 - JDK 17+ - Maven 3.6+ - MySQL 8.0+ - MinIO Server ### 1. 克隆项目 ```bash git clone http://ldfun.asia:8418/djl1022/java.git cd java ``` ### 2. 编译项目 ```bash mvn clean install -DskipTests ``` ### 3. 配置数据库与 MinIO 确保 MySQL 数据库 `wangpan` 已创建,MinIO 服务已启动。配置文件中的密码均使用 Jasypt 加密,无需手动修改。 ### 4. 启动服务 **必须通过以下命令启动每个服务**(Jasypt 解密密钥必须传入): ```bash # 启动用户服务(端口 8081) java -DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 -jar wangpan-user-service/target/wangpan-user-service-1.0-SNAPSHOT.jar # 启动网盘服务(端口 8082) java -DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 -jar wangpan-drive-service/target/wangpan-drive-service-1.0-SNAPSHOT.jar # 启动 TMDB 服务(端口 8083) java -DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 -jar wangpan-tmdb-service/target/wangpan-tmdb-service-1.0-SNAPSHOT.jar # 启动网关(端口 8888) java -DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 -jar wangpan-gateway/target/wangpan-gateway-1.0-SNAPSHOT.jar ``` > **注意**:`-DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024` 是解密配置文件中加密密码的密钥,**必须传入**,否则服务启动会报错。 ### 5. 访问系统 - **API 网关**:http://localhost:8888 - **API 文档页面**:http://localhost:8888/api-doc.html - **各服务 Swagger**:http://localhost:8081/swagger-ui.html 等 --- ## 配置说明 ### Jasypt 配置加密 所有敏感信息(数据库密码、MinIO 密钥、TMDB API Key)均使用 Jasypt 加密存储,格式为 `ENC(加密后的密文)`。 **所有服务统一配置**: ```yaml jasypt: encryptor: password: ${JASYPT_ENCRYPTOR_PASSWORD} # 必须通过环境变量或 -D 参数传入 algorithm: PBEWITHHMACSHA512ANDAES_256 # 统一使用强加密算法 key-obtention-iterations: 1000 pool-size: 1 salt-generator-classname: org.jasypt.salt.RandomSaltGenerator iv-generator-classname: org.jasypt.iv.RandomIvGenerator string-output-type: base64 ``` **加密密钥**:`wangpan-secret-key-2024` ### 数据库连接 - **地址**:`118.178.238.159:33306` - **数据库名**:`wangpan` - **连接参数**:`useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true` ### MinIO 对象存储 - **地址**:`118.178.238.159:39000` - **存储桶**:`wangpan`(网盘文件)、`tmdb`(TMDB 缓存数据) ### 网关路由 | 路由路径 | 转发目标 | |---------|---------| | `/api/login`, `/api/register` | `http://localhost:8081` | | `/api/token/**`, `/api/login-logs/**` | `http://localhost:8081` | | `/api/users/**` | `http://localhost:8081` | | `/api/drive/**` | `http://localhost:8082` | | `/api/icon/**` | `http://localhost:8082` | | `/api/movies/**` | `http://localhost:8083` | | `/api/tmdb/**`, `/api/tmdb-scraper/**` | `http://localhost:8083` | 所有路由 `StripPrefix=0`(不裁剪路径前缀)。 --- ## API 接口文档 完整 API 文档请访问:**http://localhost:8888/api-doc.html** ### 用户服务(端口 8081) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | `/api/login` | 用户登录 | 否 | | POST | `/api/register` | 用户注册 | 否 | | GET | `/api/token/validate` | 验证 Token | 否 | | GET | `/api/login-logs` | 查询登录日志 | 需Token | | GET | `/api/users` | 查询用户列表 | 需Token | | POST | `/api/users` | 创建用户 | 需Token | | PUT | `/api/users/{userId}` | 更新用户 | 需Token | | DELETE | `/api/users/{userId}` | 删除用户 | 需Token | ### 网盘服务(端口 8082) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | `/api/drive` | 创建网盘 | 需Token | | GET | `/api/drive` | 查询网盘列表 | 需Token | | GET | `/api/drive/{id}` | 查询网盘详情 | 需Token | | PUT | `/api/drive/{id}` | 更新网盘 | 需Token | | DELETE | `/api/drive/{id}` | 删除网盘 | 需Token | | POST | `/api/drive/{driveId}/folders` | 创建文件夹 | 需Token | | GET | `/api/drive/{driveId}/folders` | 查询文件夹列表 | 需Token | | GET | `/api/drive/{driveId}/folders/{folderId}` | 查询文件夹详情 | 需Token | | PUT | `/api/drive/{driveId}/folders/{folderId}` | 更新文件夹 | 需Token | | DELETE | `/api/drive/{driveId}/folders/{folderId}` | 删除文件夹 | 需Token | | POST | `/api/drive/{driveId}/movies` | 添加电影 | 需Token | | GET | `/api/drive/{driveId}/folders/{folderId}/movies` | 查询电影列表 | 需Token | | GET | `/api/drive/{driveId}/movies/{movieId}` | 查询电影详情 | 需Token | | PUT | `/api/drive/{driveId}/movies/{movieId}` | 更新电影 | 需Token | | DELETE | `/api/drive/{driveId}/movies/{movieId}` | 删除电影 | 需Token | | GET | `/api/drive/movies/duplicates` | 检查重复电影 | 需Token | | POST | `/api/icon` | 创建图标 | 需Token | | GET | `/api/icon` | 查询图标列表 | 需Token | | GET | `/api/icon/{id}` | 查询图标详情 | 需Token | | PUT | `/api/icon/{id}` | 更新图标 | 需Token | | DELETE | `/api/icon/{id}` | 删除图标 | 需Token | | POST | `/api/icon/{id}/logo` | 上传图标 Logo | 需Token | ### TMDB 服务(端口 8083) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | GET | `/api/movies` | 查询电影列表 | 需Token | | GET | `/api/movies/recommend` | 推荐电影 | 需Token | | GET | `/api/movies/{id}` | 查询电影详情 | 需Token | | POST | `/api/movies` | 创建电影 | 需Token | | PUT | `/api/movies/{id}` | 更新电影 | 需Token | | DELETE | `/api/movies/{id}` | 删除电影 | 需Token | | POST | `/api/movies/batch-delete` | 批量删除电影 | 需Token | | GET | `/api/movies/stats` | 电影统计 | 需Token | | GET | `/api/tmdb/key` | 查询 TMDB API Key | 需Token | | POST | `/api/tmdb/key` | 设置 TMDB API Key | 需Token | | POST | `/api/tmdb-scraper/scrape` | 单部电影刮削 | 需Token | | POST | `/api/tmdb-scraper/batch-scrape` | 批量刮削 | 需Token | | GET | `/api/tmdb-scraper/caches` | 查询刮削缓存 | 需Token | | GET | `/api/tmdb-scraper/stats` | 刮削统计 | 需Token | | POST | `/api/tmdb-scraper/trigger` | 手动触发定时任务 | 需Token | | GET | `/api/tmdb-scraper/scraper-logs` | 查询刮削日志 | 需Token | | DELETE | `/api/tmdb-scraper/cache/{movieId}` | 删除缓存 | 需Token | ### 统一响应格式 ```json { "code": 200, "message": "操作成功", "data": {} } ``` | 状态码 | 含义 | |--------|------| | 200 | 操作成功 | | 400 | 参数错误 | | 401 | 未登录 / Token 过期 | | 403 | 权限不足 | | 404 | 资源不存在 | | 429 | 请求过于频繁 | | 500 | 服务器内部错误 | --- ## 数据库设计 ### 核心表结构 | 表名 | 所属模块 | 说明 | |------|---------|------| | `wp_user` | user-service | 用户表 | | `wp_drive` | drive-service | 网盘表 | | `wp_drive_folder` | drive-service | 文件夹表 | | `wp_drive_movie` | drive-service | 电影表(唯一电影数据表) | | `wp_tmdb_cache` | tmdb-service | TMDB 刮削缓存表 | | `wp_tmdb_scraper_log` | tmdb-service | 刮削日志表 | | `wp_tmdb_config` | tmdb-service | TMDB API 配置表 | | `wp_login_log` | user-service | 登录日志表 | | `wp_icon` | drive-service | 图标表 | ### 表命名规范 - 所有表以 `wp_` 前缀开头 - 表名使用小写 + 下划线分隔 - 主键统一为 `id`(BIGINT AUTO_INCREMENT) - 状态字段使用字符串枚举值(如 `active`, `deleted`) - 索引命名:`idx_字段名` ### 重要约定 > **`wp_drive_movie` 是项目中唯一的电影数据表**,所有模块(drive-service、tmdb-service)都操作同一张表。不存在 `wp_movie` 表(已废弃)。 --- ## 项目规范 ### 命名规范 #### 包结构 ``` com.wangpan ├── common/ # 公共模块 │ ├── annotation/ # 自定义注解(@RequireAuth, @RequireAdmin) │ ├── config/ # 配置类 │ ├── dal/ # 数据访问层 │ │ ├── dataobject/ # DO 数据对象 │ │ ├── mapper/ # MyBatis Mapper 接口 │ │ └── repository/ # Repository 封装 │ ├── entity/ # 业务实体 │ ├── interceptor/ # 拦截器 │ ├── listener/ # 定时任务 │ └── util/ # 工具类 ├── controller/ # 控制器(各子模块独立) ├── service/ # 业务服务(各子模块独立) └── Application.java # 启动类(各子模块独立) ``` #### 类命名 | 类型 | 规则 | 示例 | |------|------|------| | DO | `{实体名}DO` | `DriveMovieDO`, `UserDO` | | Mapper | `{实体名}Mapper` | `DriveMovieMapper`, `UserMapper` | | Repository | `{实体名}Repository` | `DriveMovieRepository` | | Entity | `{实体名}` | `DriveMovie`, `User` | | Controller | `{模块名}Controller` | `DriveController`, `AuthController` | | Service | `{模块名}Service` | `DriveService`, `TmdbScraperService` | | 工具类 | `{功能}Util` | `TokenUtil`, `MinioUtil`, `EncryptUtil` | | 配置类 | `{功能}Config` | `WebMvcConfig`, `MyBatisPlusConfig` | | 定时任务 | `{功能}Scheduler` | `TmdbScheduler`, `IconCleanupScheduler` | #### 方法命名 | 操作 | 规则 | 示例 | |------|------|------| | 查询单个 | `findBy{条件}` | `findById`, `findByMovieId` | | 查询列表 | `queryBy{条件}` | `queryByUserId`, `queryByDriveAndFolder` | | 统计 | `countBy{条件}` | `countByDrive`, `countByUser` | | 插入 | `insert` | `insert(DriveMovie movie)` | | 更新 | `update` / `update{字段}` | `update`, `updateScrapeInfo` | | 逻辑删除 | `logicalDelete` | `logicalDelete(Long id, Long userId)` | | 物理删除 | `delete` | `delete(Long id, Long userId)` | ### 编码规范 1. **Controller** 直接返回 `Result` 对象,不返回原始数据 2. **Repository** 继承 `ServiceImpl`,自动获得 CRUD 能力 3. **Mapper** 继承 `BaseMapper`,简单 CRUD 自动生成 4. **复杂 SQL** 写在 Mapper XML 中,不写在 Java 代码中 5. **数据库操作** 使用 `LambdaQueryWrapper` / `LambdaUpdateWrapper`,不拼接 SQL 字符串 6. **认证** 使用 `@RequireAuth` 和 `@RequireAdmin` 注解 7. **当前用户** 通过 `request.getAttribute("currentUser")` 获取 ### MyBatis XML 映射规则 **列别名一致性**:XML 中 `SELECT` 的列别名必须与 Java 代码中 `Map.get()` 的 key 完全一致。 ```xml SELECT title, COUNT(*) AS cnt FROM wp_drive_movie ... ``` ```java // Java 中必须使用相同的 key g.get("cnt") // ✅ 正确 g.get("count") // ❌ 错误,别名是 cnt 不是 count ``` **数值安全转换**:`resultType="java.util.Map"` 返回的数值,使用 `((Number) g.get("cnt")).intValue()` 安全转换。 ### 配置文件规范 - 所有敏感信息使用 Jasypt `ENC(...)` 加密 - 所有服务 Jasypt 加密算法统一为 `PBEWITHHMACSHA512ANDAES_256` - 日志级别生产环境设为 `INFO` - 不启用 MyBatis-Plus SQL 日志输出到控制台 - Mapper XML 路径使用 `classpath*:` 前缀(跨模块加载) --- ## 部署指南 ### 生产环境环境变量 ```bash # Linux / Mac export JASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 # Windows PowerShell $env:JASYPT_ENCRYPTOR_PASSWORD="wangpan-secret-key-2024" ``` ### Docker 部署 ```bash docker run -d \ -e JASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 \ -p 8081:8081 \ wangpan-user-service:latest ``` ### systemd 服务(Linux) ```ini [Unit] Description=WangPan User Service [Service] Environment=JASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 ExecStart=/usr/bin/java -jar /opt/wangpan/wangpan-user-service.jar Restart=on-failure [Install] WantedBy=multi-user.target ``` ### 启动顺序 1. MySQL、MinIO(确保已启动) 2. `wangpan-user-service`(端口 8081) 3. `wangpan-drive-service`(端口 8082) 4. `wangpan-tmdb-service`(端口 8083) 5. `wangpan-gateway`(端口 8888) --- ## 常见问题 ### Q1:启动报错 `Failed to bind properties under 'spring.datasource.password'` **原因**:Jasypt 解密密钥未正确传入。 **解决**:使用 `-DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024` 参数启动,不要用 `$env:xxx` 方式设置环境变量(Windows 下 `Start-Process` 子进程不继承)。 ### Q2:TMDB 刮削返回"没有需要刮削的电影" **原因**:`wp_drive_movie` 表中没有电影数据,或现有电影的 `scrapeType` 字段为 `none`。 **解决**:先通过网盘服务上传电影文件,或通过 `POST /api/movies` 创建电影记录,确保 `scrapeType` 不为 `none`。 ### Q3:API 返回 401 未授权 **原因**:Token 过期或未登录。 **解决**:先调用 `POST /api/login` 获取 Token,在请求头中携带 `Authorization: Bearer `。 ### Q4:如何修改数据库密码 1. 修改原始密码后,使用 Jasypt 命令行工具重新加密 2. 将加密后的密文替换 `application.yml` 中的 `ENC(...)` 值 3. 重启服务 ### Q5:网关路由不生效 **原因**:路由配置中 `StripPrefix=0`,且后端服务路径必须与网关路由完全匹配。 **解决**:检查 `wangpan-gateway/application.yml` 中的路由配置,确保转发目标正确。 --- ## 项目参考文件 - [PROJECT_REFERENCE.md](./PROJECT_REFERENCE.md) — 项目全局参考文件(供 AI 辅助开发使用) - [api-doc.html](./wangpan-gateway/src/main/resources/META-INF/resources/api-doc.html) — 在线 API 文档 --- ## 许可证 内部项目,仅供学习与个人使用。