java/README.md
liRQ fba79655c4 docs: 新增 README.md 项目文档
- 项目简介、技术栈、模块架构说明

- 快速开始与环境配置指南

- 完整 API 接口列表(含认证说明)

- 数据库设计与核心表结构

- 项目命名规范与编码规范

- 生产环境部署指南Docker/systemd

- 5 个常见问题排查
2026-06-18 19:08:44 +08:00

482 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<T>` 对象,不返回原始数据
2. **Repository** 继承 `ServiceImpl<Mapper, DO>`,自动获得 CRUD 能力
3. **Mapper** 继承 `BaseMapper<DO>`,简单 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
<!-- 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` 子进程不继承)。
### Q2TMDB 刮削返回"没有需要刮削的电影"
**原因**`wp_drive_movie` 表中没有电影数据,或现有电影的 `scrapeType` 字段为 `none`
**解决**:先通过网盘服务上传电影文件,或通过 `POST /api/movies` 创建电影记录,确保 `scrapeType` 不为 `none`
### Q3API 返回 401 未授权
**原因**Token 过期或未登录。
**解决**:先调用 `POST /api/login` 获取 Token在请求头中携带 `Authorization: Bearer <token>`
### 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 文档
---
## 许可证
内部项目,仅供学习与个人使用。