- MovieDO/MovieMapper 改为映射 wp_drive_movie 表 - 移除 wp_movie 建表逻辑,由 drive-service 统一管理 - 修复 TMDB 刮削查询条件,放开 scrape_type=none 限制 - 修复 api-doc.html 中 9 处与实际代码不一致 - 统一所有服务 Jasypt 加密算法 - 移除默认加密密钥,生产环境需通过环境变量设置 - 新增 PROJECT_REFERENCE.md 项目规范文件 - 新增 IconCleanupScheduler 定时清理图片 - 调整日志级别为 INFO,移除 MyBatis-Plus SQL 日志
475 lines
18 KiB
Markdown
475 lines
18 KiB
Markdown
# 网盘系统 (WangPan) - 项目全局参考文件
|
||
|
||
> **用途**:本文档记录项目规范、命名约定、常见问题与解决方案,供 TraeAI 在每次调整代码前阅读参考。
|
||
>
|
||
> **规则**:每次修改项目代码前,TraeAI 必须读取本文件以确保遵循项目规范。
|
||
|
||
---
|
||
|
||
## 一、项目概览
|
||
|
||
| 项目 | 说明 |
|
||
|------|------|
|
||
| 名称 | WangPan(网盘系统) |
|
||
| 架构 | 微服务(Spring Boot 3.2.5 + Spring Cloud Gateway) |
|
||
| 语言 | Java 17 |
|
||
| 构建工具 | Maven |
|
||
| 包名根路径 | `com.wangpan` |
|
||
| 数据库 | MySQL 8.0.33 |
|
||
| 对象存储 | MinIO |
|
||
| ORM | MyBatis-Plus 3.5.7 |
|
||
| 配置加密 | Jasypt(Jasypt Spring Boot Starter) |
|
||
|
||
### 模块结构与端口
|
||
|
||
| 模块 | 端口 | 说明 |
|
||
|------|------|------|
|
||
| `wangpan-common` | - | 公共模块(DO/Mapper/Repository/Entity/Util/Interceptor) |
|
||
| `wangpan-user-service` | 8081 | 用户认证服务(登录、注册、Token管理) |
|
||
| `wangpan-drive-service` | 8082 | 网盘管理服务(文件管理、电影管理) |
|
||
| `wangpan-tmdb-service` | 8083 | TMDB 刮削服务(电影元数据抓取) |
|
||
| `wangpan-gateway` | 8888 | API 网关(统一路由入口) |
|
||
|
||
---
|
||
|
||
## 二、命名规范
|
||
|
||
### 2.1 包命名
|
||
|
||
```
|
||
com.wangpan # 根包
|
||
├── common # 公共模块
|
||
│ ├── annotation # 自定义注解
|
||
│ ├── config # 配置类
|
||
│ ├── dal # 数据访问层
|
||
│ │ ├── dataobject # 数据对象(DO)
|
||
│ │ ├── mapper # MyBatis Mapper 接口
|
||
│ │ └── repository # Repository 层(封装数据访问)
|
||
│ ├── entity # 业务实体
|
||
│ ├── interceptor # 拦截器
|
||
│ ├── listener # 监听器 / 定时任务
|
||
│ └── util # 工具类
|
||
├── controller # 控制器(各子模块各自持有)
|
||
├── service # 业务服务层(各子模块各自持有)
|
||
└── Application.java # 启动类(各子模块各自持有)
|
||
```
|
||
|
||
**规则**:
|
||
- 各子服务的 Controller 和 Service 放在各自模块的 `src/main/java/com/wangpan/` 下
|
||
- 公共的 DO、Mapper、Repository、Entity、Util 放在 `wangpan-common` 中
|
||
- 所有 Mapper XML 文件统一放在 `wangpan-common/src/main/resources/com/wangpan/common/dal/mapper/` 下
|
||
|
||
### 2.2 类命名
|
||
|
||
| 类型 | 命名规则 | 示例 |
|
||
|------|---------|------|
|
||
| DO(数据对象) | `{实体名}DO` | `DriveMovieDO`, `UserDO`, `MovieDO` |
|
||
| Mapper 接口 | `{实体名}Mapper` | `DriveMovieMapper`, `UserMapper` |
|
||
| Repository | `{实体名}Repository` | `DriveMovieRepository`, `UserRepository` |
|
||
| Entity(业务实体) | `{实体名}` | `DriveMovie`, `User`, `Movie` |
|
||
| Controller | `{模块名}Controller` | `DriveController`, `AuthController`, `TmdbController` |
|
||
| Service | `{模块名}Service` | `DriveService`, `UserService`, `TmdbService` |
|
||
| 工具类 | `{功能}Util` | `TokenUtil`, `MinioUtil`, `AuthUtil`, `EncryptUtil` |
|
||
| 配置类 | `{功能}Config` | `WebMvcConfig`, `MyBatisPlusConfig` |
|
||
| 启动类 | `{模块名}Application` | `UserServiceApplication`, `DriveServiceApplication` |
|
||
| 拦截器 | `{功能}Interceptor` | `AuthInterceptor` |
|
||
| 定时任务 | `{功能}Scheduler` | `TmdbScheduler`, `IconCleanupScheduler` |
|
||
|
||
### 2.3 方法命名
|
||
|
||
| 操作 | 命名规则 | 示例 |
|
||
|------|---------|------|
|
||
| 查询单个 | `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)` |
|
||
| 移动 | `moveTo{目标}` | `moveToFolder` |
|
||
|
||
### 2.4 数据库命名
|
||
|
||
| 元素 | 命名规则 | 示例 |
|
||
|------|---------|------|
|
||
| 表名 | `wp_{模块}_{实体}` | `wp_drive_movie`, `wp_user`, `wp_tmdb_cache` |
|
||
| 字段名 | 小写 + 下划线分隔 | `drive_id`, `user_id`, `create_time`, `movie_title` |
|
||
| 主键 | `id`(BIGINT AUTO_INCREMENT) | `id` |
|
||
| 索引 | `idx_{字段名}` | `idx_drive_id`, `idx_user_id`, `idx_status` |
|
||
| 状态字段 | 字符串枚举值 | `'active'`, `'deleted'`, `'expired'` |
|
||
|
||
### 2.5 API 命名
|
||
|
||
| 模块 | 路径前缀 | 示例 |
|
||
|------|---------|------|
|
||
| 用户服务 | `/api/login`, `/api/register`, `/api/token/**`, `/api/users/**`, `/api/login-logs/**` | `POST /api/login` |
|
||
| 网盘服务 | `/api/drive/**`, `/api/icon/**` | `GET /api/drive/movies/duplicates` |
|
||
| TMDB服务 | `/api/movies/**`, `/api/tmdb/**`, `/api/tmdb-scraper/**` | `GET /api/movies/{id}` |
|
||
|
||
---
|
||
|
||
## 三、技术架构模式
|
||
|
||
### 3.1 分层架构
|
||
|
||
```
|
||
Controller → 接收请求、参数校验、调用 Service、返回 Result
|
||
↓
|
||
Service → 业务逻辑处理、事务管理
|
||
↓
|
||
Repository → 数据访问封装、Entity ↔ DO 转换、MyBatis-Plus LambdaQueryWrapper 查询
|
||
↓
|
||
Mapper → MyBatis-Plus BaseMapper 接口 + XML 复杂 SQL
|
||
↓
|
||
DO → 数据库表映射(@TableName 注解)
|
||
```
|
||
|
||
**关键规则**:
|
||
- Controller 直接返回 `Result<T>` 对象,不要直接返回原始数据
|
||
- Repository 继承 `ServiceImpl<Mapper, DO>`,自动获得 MyBatis-Plus CRUD 能力
|
||
- Mapper 继承 `BaseMapper<DO>`,简单 CRUD 自动生成
|
||
- 复杂 SQL 写在 Mapper XML 中,方法签名定义在 Mapper 接口中
|
||
- Repository 负责 Entity ↔ DO 转换,对外暴露 Entity 或 Map 类型
|
||
|
||
### 3.2 Result 统一响应
|
||
|
||
```java
|
||
// 成功
|
||
Result.success("操作成功", data);
|
||
|
||
// 各类错误
|
||
Result.badRequest("参数错误"); // 400
|
||
Result.unauthorized("登录已过期"); // 401
|
||
Result.forbidden("权限不足"); // 403
|
||
Result.notFound("资源不存在"); // 404
|
||
Result.tooManyRequests("请求过于频繁"); // 429
|
||
Result.error("服务器错误"); // 500
|
||
Result.error(code, "自定义错误"); // 自定义状态码
|
||
```
|
||
|
||
### 3.3 认证模式
|
||
|
||
- 使用 `@RequireAuth` 注解标记需要登录的接口
|
||
- 使用 `@RequireAdmin` 注解标记需要管理员权限的接口
|
||
- `AuthInterceptor` 拦截器统一处理认证逻辑
|
||
- Controller 通过 `request.getAttribute("currentUser")` 获取当前用户
|
||
- Token 使用 `TokenUtil` 生成和验证
|
||
|
||
### 3.4 MyBatis-Plus 查询模式
|
||
|
||
```java
|
||
// 简单查询 → LambdaQueryWrapper
|
||
var wrapper = new LambdaQueryWrapper<DriveMovieDO>()
|
||
.eq(DriveMovieDO::getDriveId, driveId)
|
||
.eq(DriveMovieDO::getUserId, userId)
|
||
.eq(DriveMovieDO::getStatus, "active")
|
||
.orderByDesc(DriveMovieDO::getCreateTime);
|
||
|
||
// 分页查询
|
||
var page = new Page<DriveMovieDO>(pageNo, pageSize);
|
||
var result = page(page, wrapper);
|
||
|
||
// 更新 → LambdaUpdateWrapper
|
||
var wrapper = new LambdaUpdateWrapper<DriveMovieDO>()
|
||
.eq(DriveMovieDO::getId, id)
|
||
.set(DriveMovieDO::getStatus, "deleted");
|
||
```
|
||
|
||
### 3.5 Jasypt 配置加密模式
|
||
|
||
所有服务(必须一致):
|
||
```yaml
|
||
jasypt:
|
||
encryptor:
|
||
password: ${JASYPT_ENCRYPTOR_PASSWORD} # 必须通过环境变量传入
|
||
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`
|
||
|
||
**启动命令**(必须带 Jasypt 密钥参数):
|
||
```bash
|
||
java -DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 -jar xxx.jar
|
||
```
|
||
|
||
### 3.6 MinIO 存储模式
|
||
|
||
- `MinioUtil` 作为 Spring Bean,使用 `@PostConstruct` 初始化
|
||
- 支持静态方法调用和 Spring Bean 注入两种方式
|
||
- 预签名 URL 有效期:7 天
|
||
- 存储桶按服务区分:`wangpan`(网盘服务)、`tmdb`(TMDB服务)
|
||
|
||
### 3.7 定时任务模式
|
||
|
||
```java
|
||
@Component
|
||
public class TmdbScheduler {
|
||
@Scheduled(fixedDelay = 6 * 60 * 60 * 1000, initialDelay = 5 * 60 * 1000)
|
||
public void executeScrape() { ... }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 四、MyBatis XML 映射规则(重要!)
|
||
|
||
### 4.1 列别名规则
|
||
|
||
XML 中 `SELECT` 返回的列别名 **必须与 Java 代码中 `Map.get()` 使用的 key 完全一致**:
|
||
|
||
```xml
|
||
<!-- XML 中 -->
|
||
<select id="findDuplicateMovies" resultType="java.util.Map">
|
||
SELECT title, COUNT(*) AS cnt <!-- 注意:列别名是 cnt -->
|
||
FROM wp_drive_movie
|
||
...
|
||
</select>
|
||
```
|
||
|
||
```java
|
||
// Java 代码中必须使用相同的 key
|
||
g.get("cnt") // ✅ 正确 - 与 XML 别名 cnt 一致
|
||
g.get("count") // ❌ 错误 - 别名是 cnt 不是 count
|
||
```
|
||
|
||
### 4.2 数值类型转换
|
||
|
||
XML 中 `resultType="java.util.Map"` 返回的数值,Java 中获取时需要安全转换:
|
||
|
||
```java
|
||
// ✅ 推荐:使用 Number 父类转换
|
||
((Number) g.get("cnt")).intValue()
|
||
|
||
// ❌ 不推荐:直接强制转换可能失败
|
||
(int) g.get("cnt")
|
||
```
|
||
|
||
### 4.3 分页查询
|
||
|
||
XML 中使用 `LIMIT #{pageSize} OFFSET #{pageNo}` 时,分页偏移量计算规则:
|
||
```java
|
||
// 前端传 pageNo 从 1 开始,XML 中 offset = (pageNo - 1) * pageSize
|
||
// 或者前端直接传 offset 值
|
||
```
|
||
|
||
---
|
||
|
||
## 五、已发现的问题与解决方案
|
||
|
||
### 问题 1:SQL 列别名与 Java Map key 不匹配导致 NPE
|
||
|
||
**日期**:2026-06-15
|
||
|
||
**问题描述**:调用 `GET /api/drive/movies/duplicates` 返回 500,报错:
|
||
```
|
||
java.lang.NullPointerException: Cannot invoke "java.lang.Integer.intValue()"
|
||
because the return value of "java.util.Map.get(Object)" is null
|
||
```
|
||
|
||
**根因**:`DriveMovieMapper.xml` 中 SQL 别名是 `cnt`,但 `DriveController.java` 中使用的 key 是 `count`。
|
||
|
||
**解决方案**:
|
||
1. 将 Controller 中的 `g.get("count")` 改为 `g.get("cnt")`
|
||
2. 使用安全转换 `((Number) g.get("cnt")).intValue()` 替代 `(int) g.get("cnt")`
|
||
|
||
**影响文件**:
|
||
- `wangpan-drive-service/.../controller/DriveController.java` 第 676 行
|
||
- `wangpan-common/.../mapper/DriveMovieMapper.xml` 第 10 行(SQL 别名 `cnt`)
|
||
|
||
**教训**:修改 XML 中的 SQL 列别名时,必须同步修改所有引用该结果集的 Java 代码中的 Map key。
|
||
|
||
### 问题 2:网关 Jasypt 加密算法不一致
|
||
|
||
**日期**:2026-06-15
|
||
|
||
**问题描述**:网关服务使用 `PBEWithMD5AndDES` 算法,其他服务使用 `PBEWITHHMACSHA512ANDAES_256`,不一致且 MD5+DES 算法已过时。
|
||
|
||
**解决方案**:统一所有服务使用 `PBEWITHHMACSHA512ANDAES_256`。
|
||
|
||
**影响文件**:`wangpan-gateway/src/main/resources/application.yml`
|
||
|
||
### 问题 3:硬编码默认 Jasypt 密钥
|
||
|
||
**日期**:2026-06-15
|
||
|
||
**问题描述**:所有服务配置了 `${JASYPT_ENCRYPTOR_PASSWORD:wangpan-secret-key-2024}` 默认值,存在安全风险。
|
||
|
||
**解决方案**:移除默认值,改为 `${JASYPT_ENCRYPTOR_PASSWORD}`,启动时强制通过命令行参数 `-DJASYPT_ENCRYPTOR_PASSWORD=xxx` 传入。
|
||
|
||
**影响文件**:所有服务的 `application.yml`
|
||
|
||
### 问题 4:日志级别过高
|
||
|
||
**日期**:2026-06-15
|
||
|
||
**问题描述**:所有服务日志级别为 `DEBUG`,生产环境会产生大量日志。
|
||
|
||
**解决方案**:改为 `INFO` 级别。
|
||
|
||
### 问题 5:MyBatis-Plus SQL 日志输出到控制台
|
||
|
||
**日期**:2026-06-15
|
||
|
||
**问题描述**:配置了 `log-impl: org.apache.ibatis.logging.stdout.StdOutImpl`,生产环境会打印所有 SQL。
|
||
|
||
**解决方案**:移除该配置。
|
||
|
||
### 问题 6:启动时 Jasypt 环境变量未传递
|
||
|
||
**日期**:2026-06-15
|
||
|
||
**问题描述**:使用 `$env:JASYPT_ENCRYPTOR_PASSWORD="xxx"` + `Start-Process` 启动服务时,子进程未继承环境变量,导致 `Failed to bind properties under 'spring.datasource.password'`。
|
||
|
||
**解决方案**:使用 `-DJASYPT_ENCRYPTOR_PASSWORD=xxx` 命令行参数替代环境变量。
|
||
|
||
### 问题 7:TMDB 刮削查询了错误的电影表
|
||
|
||
**日期**:2026-06-18
|
||
|
||
**问题描述**:TMDB 刮削器(`MovieMapper`/`MovieDO`/`MovieRepository`)映射的是 `wp_movie` 表,但项目中实际使用的电影表是 `wp_drive_movie`(由 drive-service 管理)。`wp_movie` 表无数据,导致刮削始终返回"没有需要刮削的电影"。
|
||
|
||
**根因**:项目中存在两张结构相同的电影表(`wp_movie` 和 `wp_drive_movie`),但实际只有 `wp_drive_movie` 有数据。
|
||
|
||
**解决方案**:
|
||
1. 将 `MovieDO` 的 `@TableName` 从 `wp_movie` 改为 `wp_drive_movie`
|
||
2. 将所有 `MovieMapper` 的 SQL(含 XML 和注解)中的 `wp_movie` 改为 `wp_drive_movie`
|
||
3. 将 `TmdbCacheMapper.xml` 中 JOIN 的 `wp_movie` 改为 `wp_drive_movie`
|
||
4. 移除 `MovieMapper.CREATE_TABLE_SQL`(`wp_drive_movie` 由 `DriveMovieMapper` 负责建表)
|
||
5. 从 tmdb-service 的 `DatabaseInitializer` 中移除 `wp_movie` 表创建
|
||
6. 修改 `findMoviesNeedingScrape` 查询条件,移除 `scrape_type != 'none'` 限制,允许未刮削的电影被选中
|
||
|
||
**影响文件**:
|
||
- `wangpan-common/.../dataobject/MovieDO.java` — `@TableName` 改为 `wp_drive_movie`
|
||
- `wangpan-common/.../mapper/MovieMapper.java` — 移除 `CREATE_TABLE_SQL`,SQL 改表名
|
||
- `wangpan-common/.../mapper/MovieMapper.xml` — SQL 表名改为 `wp_drive_movie`,放开 `scrape_type` 限制
|
||
- `wangpan-common/.../mapper/TmdbCacheMapper.xml` — JOIN 表名改为 `wp_drive_movie`
|
||
- `wangpan-common/.../mapper/TmdbCacheMapper.java` — 注释更新
|
||
- `wangpan-common/.../entity/Movie.java` — 注释更新
|
||
- `wangpan-tmdb-service/.../config/DatabaseInitializer.java` — 移除 `wp_movie` 建表
|
||
- `wangpan-tmdb-service/.../service/TmdbScraperService.java` — 注释更新
|
||
|
||
**教训**:项目中统一使用 `wp_drive_movie` 作为电影表,所有模块(drive-service、tmdb-service)都应操作同一张表。`wp_movie` 表已被废弃,不应再使用。
|
||
|
||
---
|
||
|
||
## 六、配置文件关键信息
|
||
|
||
### 6.1 数据库连接
|
||
|
||
- 地址:`118.178.238.159:33306`
|
||
- 数据库名:`wangpan`
|
||
- 连接参数:`useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true`
|
||
- 所有密码使用 Jasypt `ENC(...)` 加密
|
||
|
||
### 6.2 MinIO 连接
|
||
|
||
- 地址:`118.178.238.159:39000`
|
||
- 访问密钥和秘密密钥使用 Jasypt `ENC(...)` 加密
|
||
- 存储桶:`wangpan`(网盘服务)、`tmdb`(TMDB服务)
|
||
|
||
### 6.3 网关路由规则
|
||
|
||
| 路由路径 | 转发目标 |
|
||
|---------|---------|
|
||
| `/api/login`, `/api/register`, `/api/token/**`, `/api/login-logs/**`, `/api/users/**` | `http://localhost:8081` |
|
||
| `/api/drive/**`, `/api/icon/**` | `http://localhost:8082` |
|
||
| `/api/movies/**`, `/api/tmdb/**`, `/api/tmdb-scraper/**` | `http://localhost:8083` |
|
||
|
||
所有路由 `StripPrefix=0`(不裁剪路径前缀)。
|
||
|
||
---
|
||
|
||
## 七、TraeAI 操作规则
|
||
|
||
### 7.1 每次修改代码前必须执行
|
||
|
||
1. **读取本文件**(`PROJECT_REFERENCE.md`)了解项目规范
|
||
2. 查找相关代码,理解现有模式后再修改
|
||
3. 修改代码时遵循项目命名规范
|
||
|
||
### 7.2 修改规范
|
||
|
||
1. **禁止**创建不必要的新文件,优先编辑现有文件
|
||
2. **禁止**创建文档(*.md)除非用户明确要求
|
||
3. 修改 SQL 列别名时,**必须同步检查**所有 Java 代码中对 Map key 的引用
|
||
4. 修改配置文件时,**确保所有服务配置一致**(Jasypt 算法、日志级别等)
|
||
5. 使用 `Result` 类统一返回,不要返回原始类型
|
||
6. 数据库操作使用 MyBatis-Plus LambdaQueryWrapper/LambdaUpdateWrapper,不要拼接 SQL 字符串
|
||
7. 复杂 SQL 写在 Mapper XML 中,不要写在 Java 代码中
|
||
|
||
### 7.3 编译与启动
|
||
|
||
```bash
|
||
# 编译
|
||
mvn clean install -DskipTests
|
||
|
||
# 启动单个服务
|
||
java -DJASYPT_ENCRYPTOR_PASSWORD=wangpan-secret-key-2024 -jar xxx\target\xxx-1.0-SNAPSHOT.jar
|
||
```
|
||
|
||
### 7.4 遇到新问题时
|
||
|
||
在本文档 **第五章** 中追加新问题记录,包含:
|
||
- 日期
|
||
- 问题描述
|
||
- 根因
|
||
- 解决方案
|
||
- 影响文件
|
||
- 教训
|
||
|
||
---
|
||
|
||
## 八、MyBatis-Plus 配置要点
|
||
|
||
```yaml
|
||
mybatis-plus:
|
||
mapper-locations: classpath*:com/wangpan/common/dal/mapper/**/*.xml
|
||
type-aliases-package: com.wangpan.common.dal.dataobject
|
||
configuration:
|
||
map-underscore-to-camel-case: true # 自动驼峰映射
|
||
global-config:
|
||
db-config:
|
||
id-type: auto # 主键自增
|
||
```
|
||
|
||
**注意**:`mapper-locations` 使用 `classpath*:` 前缀,因为 Mapper XML 在 `wangpan-common` 模块中,所有子服务都需要加载。
|
||
|
||
---
|
||
|
||
## 九、api-doc.html 维护规范
|
||
|
||
### 9.1 文件位置
|
||
`wangpan-gateway/src/main/resources/META-INF/resources/api-doc.html`
|
||
|
||
### 9.2 历史不一致问题记录
|
||
|
||
**问题 8:api-doc.html 与代码多处不一致**
|
||
|
||
**日期**:2026-06-18
|
||
|
||
**发现的不一致**:
|
||
|
||
| 接口 | 文档描述 | 实际代码 | 严重程度 |
|
||
|------|---------|---------|---------|
|
||
| `GET /api/movies` 查询参数 `status` | `pending / scraped / failed` | `active / deleted`(status 是记录状态,scrapeType 是刮削状态) | **高** |
|
||
| `GET /api/movies` 缺少参数 | 无 `sortBy`/`sortOrder` | 支持 `sortBy` 和 `sortOrder` 排序参数 | 中 |
|
||
| `POST /api/movies` 请求体 | 只有 `{ "title": "..." }` | 支持 title, subtitle, resourceLibrary, maintainer, status, category, scrapeType, rating, posterUrl, tags | **高** |
|
||
| `PUT /api/movies/{id}` 请求体 | 空白 | 支持全部字段的部分更新 | **高** |
|
||
| `GET /api/movies/stats` 响应 | 空白 | 返回 totalCount, scrapedCount, pendingCount | 中 |
|
||
| `POST /api/tmdb-scraper/scrape` | 仅请求体方式 | 同时支持 URL 参数 `?movieId=1` 和请求体 | 中 |
|
||
| `POST /api/tmdb-scraper/batch-scrape` | `movieIds` 为必填 | `movieIds` 可选,不传则自动查找 | 中 |
|
||
| 多个 `/api/tmdb-scraper/*` 端点 | 响应文档空白 | stats, trigger, scraper-logs, cache delete 等缺少响应 | 中 |
|
||
| 路由表缺少 `/api/icon/**` | 未列出 | `/api/icon/**` → drive-service | 低 |
|
||
|
||
**解决方案**:逐一修复 api-doc.html 中所有不一致的字段、参数和响应示例。
|
||
|
||
**教训**:每次新增或修改 Controller 接口时,必须同步更新 api-doc.html。
|
||
|
||
---
|
||
|
||
> **最后更新**:2026-06-18 |