feat: 完成网盘电影刮削系统全量功能开发

本次提交完成了整个网盘电影刮削系统的功能开发,包含以下核心改动:
1.  新增个人资料模块,支持用户修改用户名和头像上传
2.  实现电影刮削状态查询接口,可查看刮削进度、结果和失败原因
3.  完善每日推荐功能,支持当日固定推荐结果缓存
4.  新增图标清理定时任务,自动清理过期图标文件
5.  优化数据库查询映射,修复关联表查询错误
6.  统一各服务配置,移除调试日志并升级加密配置
7.  新增MinIO工具类和文件上传支持
8.  完善接口文档,补充所有新增API的说明示例
This commit is contained in:
liRQ 2026-06-25 18:25:08 +08:00
parent ab4ab5b797
commit 1f498d2396
57 changed files with 1034 additions and 80 deletions

View File

@ -1,5 +1,68 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="AliAccessStaticViaInstance" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliArrayNamingShouldHaveBracket" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliControlFlowStatementWithoutBraces" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliDeprecation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliEqualsAvoidNull" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliLongLiteralsEndingWithLowercaseL" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliMissingOverrideAnnotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AliWrapperTypeEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAbstractClassShouldStartWithAbstractNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAbstractMethodOrInterfaceMethodMustUseJavadoc" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidApacheBeanUtilsCopy" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidCallStaticSimpleDateFormat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidCommentBehindStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidComplexCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidConcurrentCompetitionRandom" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidDoubleOrFloatEqualCompare" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidManuallyCreateThread" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidMissUseOfMathRandom" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidNegationOperator" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidNewDateGetTime" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidPatternCompileInMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidReturnInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidStartWithDollarAndUnderLineNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaAvoidUseTimer" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaBigDecimalAvoidDoubleConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaBooleanPropertyShouldNotStartWithIs" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassCastExceptionWithSubListToArrayList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassCastExceptionWithToArray" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassMustHaveAuthor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaClassNamingShouldBeCamel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaCollectionInitShouldAssignCapacity" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaCommentsMustBeJavadocFormat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaConcurrentExceptionWithModifyOriginSubList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaConstantFieldShouldBeUpperCase" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaCountDownShouldInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaDontModifyInForeachCircle" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaEnumConstantsMustHaveComment" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaExceptionClassShouldEndWithException" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaIbatisMethodQueryForList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaLockShouldWithTryFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaLowerCamelCaseVariableNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaMethodReturnWrapperType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaMethodTooLong" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPackageNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPojoMustOverrideToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPojoMustUsePrimitiveField" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaPojoNoDefaultValue" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaRemoveCommentedCode" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaServiceOrDaoClassShouldEndWithImpl" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaSneakyThrowsWithoutExceptionType" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaStringConcat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaSwitchExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaSwitchStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaTestClassShouldEndWithTestNaming" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaThreadLocalShouldRemove" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaThreadPoolCreation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaThreadShouldSetName" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaTransactionMustHaveRollback" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUndefineMagicConstant" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUnsupportedExceptionWithModifyAsList" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUseQuietReferenceNotation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AlibabaUseRightCaseForDateFormat" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="MapOrSetKeyShouldOverrideHashCodeEquals" enabled="true" level="WARNING" enabled_by_default="true" />
</profile> </profile>
</component> </component>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

27
.idea/workspace.xml generated
View File

@ -4,7 +4,26 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="a2d7bfcd-46c4-4e1a-941c-f149d0cd7b56" name="Changes" comment="" /> <list default="true" id="a2d7bfcd-46c4-4e1a-941c-f149d0cd7b56" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/dataobject/MovieDO.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/dataobject/MovieDO.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/mapper/MovieMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/mapper/MovieMapper.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/mapper/TmdbCacheMapper.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/mapper/TmdbCacheMapper.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/repository/DriveMovieRepository.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/repository/DriveMovieRepository.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/repository/IconRepository.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/dal/repository/IconRepository.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/entity/Movie.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/entity/Movie.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/util/MinioUtil.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/java/com/wangpan/common/util/MinioUtil.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/resources/com/wangpan/common/dal/mapper/MovieMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/resources/com/wangpan/common/dal/mapper/MovieMapper.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-common/src/main/resources/com/wangpan/common/dal/mapper/TmdbCacheMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-common/src/main/resources/com/wangpan/common/dal/mapper/TmdbCacheMapper.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-drive-service/src/main/java/com/wangpan/controller/DriveController.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-drive-service/src/main/java/com/wangpan/controller/DriveController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-drive-service/src/main/java/com/wangpan/controller/IconController.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-drive-service/src/main/java/com/wangpan/controller/IconController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-drive-service/src/main/resources/application.yml" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-drive-service/src/main/resources/application.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-gateway/src/main/resources/META-INF/resources/api-doc.html" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-gateway/src/main/resources/META-INF/resources/api-doc.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-gateway/src/main/resources/application.yml" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-gateway/src/main/resources/application.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-tmdb-service/src/main/java/com/wangpan/config/DatabaseInitializer.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-tmdb-service/src/main/java/com/wangpan/config/DatabaseInitializer.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-tmdb-service/src/main/java/com/wangpan/service/TmdbScraperService.java" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-tmdb-service/src/main/java/com/wangpan/service/TmdbScraperService.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-tmdb-service/src/main/resources/application.yml" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-tmdb-service/src/main/resources/application.yml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/wangpan-user-service/src/main/resources/application.yml" beforeDir="false" afterPath="$PROJECT_DIR$/wangpan-user-service/src/main/resources/application.yml" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -15,6 +34,9 @@
<entry key="_D:/java2/wangpan/java" value="3Ekulj5UUM57tZGI3EQwJeZxtS7" /> <entry key="_D:/java2/wangpan/java" value="3Ekulj5UUM57tZGI3EQwJeZxtS7" />
</persistenceIdMap> </persistenceIdMap>
</component> </component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo">{ <component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 4 &quot;associatedIndex&quot;: 4
}</component> }</component>
@ -38,6 +60,7 @@
&quot;Spring Boot.GatewayApplication.executor&quot;: &quot;Run&quot;, &quot;Spring Boot.GatewayApplication.executor&quot;: &quot;Run&quot;,
&quot;Spring Boot.TmdbServiceApplication.executor&quot;: &quot;Run&quot;, &quot;Spring Boot.TmdbServiceApplication.executor&quot;: &quot;Run&quot;,
&quot;Spring Boot.UserServiceApplication.executor&quot;: &quot;Run&quot;, &quot;Spring Boot.UserServiceApplication.executor&quot;: &quot;Run&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;, &quot;kotlin-language-version-configured&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/java2/wangpan/java&quot;, &quot;last_opened_file_path&quot;: &quot;D:/java2/wangpan/java&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;, &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
@ -143,6 +166,8 @@
<workItem from="1781082319895" duration="1150000" /> <workItem from="1781082319895" duration="1150000" />
<workItem from="1781159460445" duration="603000" /> <workItem from="1781159460445" duration="603000" />
<workItem from="1781337048187" duration="8000" /> <workItem from="1781337048187" duration="8000" />
<workItem from="1781512008893" duration="1610000" />
<workItem from="1781769488160" duration="642000" />
</task> </task>
<servers /> <servers />
</component> </component>

View File

@ -47,6 +47,14 @@ public class DriveMovieRepository extends ServiceImpl<DriveMovieMapper, DriveMov
return movieDO != null ? toDriveMovie(movieDO) : null; return movieDO != null ? toDriveMovie(movieDO) : null;
} }
/**
* 根据ID查询电影返回 DO
* 用于需要直接访问 DO 字段的场景如刮削状态查询
*/
public DriveMovieDO findByIdAsDO(Long id) {
return getById(id);
}
/** /**
* 查询指定网盘下指定文件夹的电影列表分页 * 查询指定网盘下指定文件夹的电影列表分页
* 使用 MyBatis-Plus LambdaQueryWrapper 构建分页查询 * 使用 MyBatis-Plus LambdaQueryWrapper 构建分页查询

View File

@ -8,7 +8,7 @@
<!-- 查找用户所有网盘下的重复电影(按 title 分组,出现次数 > 1 --> <!-- 查找用户所有网盘下的重复电影(按 title 分组,出现次数 > 1 -->
<select id="findDuplicateMovies" resultType="java.util.Map"> <select id="findDuplicateMovies" resultType="java.util.Map">
SELECT title, COUNT(*) AS cnt SELECT title, COUNT(*) AS cnt
FROM wp_movie FROM wp_drive_movie
WHERE user_id = #{userId} AND status = 'active' WHERE user_id = #{userId} AND status = 'active'
GROUP BY title GROUP BY title
HAVING COUNT(*) > 1 HAVING COUNT(*) > 1
@ -24,14 +24,14 @@
AND u.status = 1 AND u.status = 1
</select> </select>
<!-- 查找指定用户需要刮削的电影 --> <!-- 查找指定用户需要刮削的电影(包含 scrape_type='none' 的未刮削电影) -->
<select id="findMoviesNeedingScrape" resultType="com.wangpan.common.dal.dataobject.MovieDO"> <select id="findMoviesNeedingScrape" resultType="com.wangpan.common.dal.dataobject.MovieDO">
SELECT m.* FROM wp_movie m SELECT m.* FROM wp_drive_movie m
LEFT JOIN wp_tmdb_cache c ON m.id = c.movie_id AND c.status = 'active' LEFT JOIN wp_tmdb_cache c ON m.id = c.movie_id AND c.status = 'active'
WHERE m.user_id = #{userId} WHERE m.user_id = #{userId}
AND m.title IS NOT NULL AND m.title != '' AND m.title IS NOT NULL AND m.title != ''
AND m.scrape_type != 'none'
AND (c.id IS NULL OR c.cache_time &lt; DATE_SUB(NOW(), INTERVAL #{expireDays} DAY)) AND (c.id IS NULL OR c.cache_time &lt; DATE_SUB(NOW(), INTERVAL #{expireDays} DAY))
AND m.status = 'active'
ORDER BY m.update_time DESC ORDER BY m.update_time DESC
LIMIT 100 LIMIT 100
</select> </select>

View File

@ -19,7 +19,7 @@
<select id="queryByUserId" resultType="java.util.Map"> <select id="queryByUserId" resultType="java.util.Map">
SELECT c.*, m.title AS movie_title SELECT c.*, m.title AS movie_title
FROM wp_tmdb_cache c FROM wp_tmdb_cache c
LEFT JOIN wp_movie m ON c.movie_id = m.id LEFT JOIN wp_drive_movie m ON c.movie_id = m.id
WHERE c.user_id = #{userId} AND c.status = 'active' WHERE c.user_id = #{userId} AND c.status = 'active'
ORDER BY c.cache_time DESC ORDER BY c.cache_time DESC
LIMIT #{pageSize} OFFSET #{pageNo} LIMIT #{pageSize} OFFSET #{pageNo}

View File

@ -21,7 +21,7 @@
<!-- 分页查询最近的执行记录 --> <!-- 分页查询最近的执行记录 -->
<select id="queryRecent" resultType="java.util.Map"> <select id="queryRecent" resultType="java.util.Map">
SELECT * FROM wp_tmdb_scraper_log ORDER BY start_time DESC SELECT * FROM wp_tmdb_scraper_log ORDER BY start_time DESC
LIMIT #{pageSize} OFFSET #{pageNo} LIMIT #{pageSize} OFFSET #{offset}
</select> </select>
<!-- 查询最近的N条执行记录 --> <!-- 查询最近的N条执行记录 -->

View File

@ -36,8 +36,10 @@ com\wangpan\common\annotation\RequireAuth.class
com\wangpan\common\dal\dataobject\LoginLogDO.class com\wangpan\common\dal\dataobject\LoginLogDO.class
com\wangpan\common\util\MinioUtil.class com\wangpan\common\util\MinioUtil.class
com\wangpan\common\dal\repository\LoginLogRepository.class com\wangpan\common\dal\repository\LoginLogRepository.class
com\wangpan\common\dal\mapper\DailyRecommendMapper.class
com\wangpan\common\dal\repository\TmdbScraperLogRepository.class com\wangpan\common\dal\repository\TmdbScraperLogRepository.class
com\wangpan\common\entity\Movie.class com\wangpan\common\entity\Movie.class
com\wangpan\common\dal\dataobject\DailyRecommendDO.class
com\wangpan\common\dal\repository\MovieRepository.class com\wangpan\common\dal\repository\MovieRepository.class
com\wangpan\common\entity\User.class com\wangpan\common\entity\User.class
com\wangpan\common\dal\mapper\DriveFolderMapper.class com\wangpan\common\dal\mapper\DriveFolderMapper.class
@ -45,5 +47,6 @@ com\wangpan\common\annotation\RequireAdmin.class
com\wangpan\common\dal\repository\TmdbCacheRepository.class com\wangpan\common\dal\repository\TmdbCacheRepository.class
com\wangpan\common\util\IconLogoFetcher.class com\wangpan\common\util\IconLogoFetcher.class
com\wangpan\common\dal\dataobject\DriveMovieDO.class com\wangpan\common\dal\dataobject\DriveMovieDO.class
com\wangpan\common\dal\repository\DailyRecommendRepository.class
com\wangpan\common\Result.class com\wangpan\common\Result.class
com\wangpan\common\dal\repository\UserRepository.class com\wangpan\common\dal\repository\UserRepository.class

View File

@ -1,48 +1,51 @@
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\MovieRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\DriveMovieDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\DriveFolderRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\SchemaUtil.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\SchemaUtil.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\AuthUtil.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\AuthUtil.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\TmdbCacheMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\TmdbScraperLogMapper.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\TmdbScraperLogMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\LoginLogDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\TokenRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\UserDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\User.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\IconRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\UserMapper.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\UserMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\DriveMovie.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\DriveMovie.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\interceptor\AuthInterceptor.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\interceptor\AuthInterceptor.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\TmdbCacheRepository.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\TmdbCacheRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\DriveFolderDO.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\DriveFolderDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\TokenUtil.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\TokenUtil.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\TmdbScraperLogDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\LoginLogRepository.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\LoginLogRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\DriveDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\IconMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\Drive.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\Drive.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\TokenMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\UserRepository.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\UserRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\Icon.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\Icon.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\IconLogoFetcher.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\IconLogoFetcher.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\DriveMapper.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\DriveMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\DriveFolder.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\DriveMovieRepository.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\DriveMovieRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\DriveRepository.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\DriveRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\Movie.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\Movie.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\MovieMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\TokenDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\annotation\RequireAdmin.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\LoginLogMapper.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\LoginLogMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\Result.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\Result.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\IconDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\TmdbScraperLogRepository.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\TmdbScraperLogRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\DailyRecommendRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\MovieDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\TmdbCacheDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\MinioUtil.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\MovieRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\DriveMovieDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\DriveFolderRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\TmdbCacheMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\LoginLogDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\TokenRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\UserDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\User.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\DailyRecommendDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\repository\IconRepository.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\TmdbScraperLogDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\DriveDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\IconMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\TokenMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\entity\DriveFolder.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\MovieMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\TokenDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\DailyRecommendMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\annotation\RequireAdmin.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\IconDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\DriveFolderMapper.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\DriveFolderMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\config\WebMvcConfig.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\config\WebMvcConfig.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\DriveMovieMapper.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\mapper\DriveMovieMapper.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\annotation\RequireAuth.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\annotation\RequireAuth.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\MovieDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\EncryptUtil.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\EncryptUtil.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\dal\dataobject\TmdbCacheDO.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\DBUtil.java D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\DBUtil.java
D:\java2\wangpan\java\wangpan-common\src\main\java\com\wangpan\common\util\MinioUtil.java

View File

@ -5,6 +5,10 @@ server:
spring: spring:
application: application:
name: wangpan-drive-service name: wangpan-drive-service
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
datasource: datasource:
url: ENC(nJXPBEsg+NwtZt+qnD9A0AwRIdPFwex6kYEfGDetJ+N42ha/dH7V3Lq/hSCYgZa08cocXV5N+AaiisG/gh+i8CcNYjXzFV8CMSRfLwx03cxxNwnDzak+2ZEY5nb8UTbC/a6b3pWuAwg9lnHF9ziroFIrOn8+s/SYN7e+oMh4tZFmfCT4h5ATsEEuB6RkYGTzf0oImBVmet1jRR4CRdHPSeh6jSWa7y1TutJoeBWyDpzHNgqE/JRLd1VNoc583/Bn) url: ENC(nJXPBEsg+NwtZt+qnD9A0AwRIdPFwex6kYEfGDetJ+N42ha/dH7V3Lq/hSCYgZa08cocXV5N+AaiisG/gh+i8CcNYjXzFV8CMSRfLwx03cxxNwnDzak+2ZEY5nb8UTbC/a6b3pWuAwg9lnHF9ziroFIrOn8+s/SYN7e+oMh4tZFmfCT4h5ATsEEuB6RkYGTzf0oImBVmet1jRR4CRdHPSeh6jSWa7y1TutJoeBWyDpzHNgqE/JRLd1VNoc583/Bn)
username: ENC(9cCYl0WIh2m3gQhAfq4ykWAm5+PUvr6yPcuGKPwYDPpADnTZKVv9eYhwT806vYti) username: ENC(9cCYl0WIh2m3gQhAfq4ykWAm5+PUvr6yPcuGKPwYDPpADnTZKVv9eYhwT806vYti)
@ -21,7 +25,7 @@ minio:
# Jasypt 加密配置 # Jasypt 加密配置
jasypt: jasypt:
encryptor: encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD:wangpan-secret-key-2024} password: ${JASYPT_ENCRYPTOR_PASSWORD}
algorithm: PBEWITHHMACSHA512ANDAES_256 algorithm: PBEWITHHMACSHA512ANDAES_256
key-obtention-iterations: 1000 key-obtention-iterations: 1000
pool-size: 1 pool-size: 1
@ -31,7 +35,7 @@ jasypt:
logging: logging:
level: level:
com.wangpan: DEBUG com.wangpan: INFO
# MyBatis-Plus 配置 # MyBatis-Plus 配置
mybatis-plus: mybatis-plus:
@ -39,7 +43,6 @@ mybatis-plus:
type-aliases-package: com.wangpan.common.dal.dataobject type-aliases-package: com.wangpan.common.dal.dataobject
configuration: configuration:
map-underscore-to-camel-case: true map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config: global-config:
db-config: db-config:
id-type: auto id-type: auto

View File

@ -1,4 +1,6 @@
com\wangpan\config\DatabaseInitializer.class com\wangpan\config\DatabaseInitializer.class
com\wangpan\controller\DriveController.class
com\wangpan\controller\IconController.class com\wangpan\controller\IconController.class
com\wangpan\listener\IconCleanupScheduler.class
com\wangpan\controller\DriveController.class
com\wangpan\DriveServiceApplication.class com\wangpan\DriveServiceApplication.class
com\wangpan\listener\IconCleanupScheduler$CleanupTask.class

View File

@ -1,4 +1,5 @@
D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\config\DatabaseInitializer.java D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\config\DatabaseInitializer.java
D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\controller\DriveController.java D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\controller\DriveController.java
D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\listener\IconCleanupScheduler.java
D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\DriveServiceApplication.java D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\DriveServiceApplication.java
D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\controller\IconController.java D:\java2\wangpan\java\wangpan-drive-service\src\main\java\com\wangpan\controller\IconController.java

View File

@ -296,6 +296,7 @@
<div class="nav-item active" data-section="overview">架构总览</div> <div class="nav-item active" data-section="overview">架构总览</div>
<div class="nav-item" data-section="user">用户服务</div> <div class="nav-item" data-section="user">用户服务</div>
<div class="nav-item" data-section="users">用户管理</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="drive">网盘服务</div>
<div class="nav-item" data-section="icon">Icon服务</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="tmdb">TMDB服务</div>
@ -347,6 +348,7 @@
<tr><td>/api/login, /api/register</td><td>user-service</td><td>登录注册</td></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/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/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/**</td><td>drive-service</td><td>网盘管理</td></tr>
<tr><td>/api/icon/**</td><td>drive-service</td><td>Icon管理</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/movies/**</td><td>tmdb-service</td><td>电影管理</td></tr>
@ -667,6 +669,118 @@
</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>
</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>
</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://118.178.238.159:39000/wangpan/avatar/1_1719234567890.png?..."</span>
}
}</div>
</div>
</div>
</div>
</div>
<!-- 网盘服务 --> <!-- 网盘服务 -->
<div class="section" id="drive"> <div class="section" id="drive">
<div class="service-card"> <div class="service-card">
@ -1544,12 +1658,19 @@
<div class="api-header" onclick="toggleDetail(this)"> <div class="api-header" onclick="toggleDetail(this)">
<span class="method GET">GET</span> <span class="method GET">GET</span>
<span class="api-path">/api/movies/recommend</span> <span class="api-path">/api/movies/recommend</span>
<span class="api-desc">今日推荐(随机推荐3部电影</span><span class="auth-badge">需Token</span> <span class="api-desc">今日推荐(每天固定3部电影</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section"> <div class="detail-section">
<h4>功能说明</h4> <h4>功能说明</h4>
<p>从当前用户的电影库中随机抽取3部电影用于首页展示每次请求返回不同的推荐结果。</p> <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>
<div class="detail-section"> <div class="detail-section">
<h4>成功响应示例</h4> <h4>成功响应示例</h4>
@ -1614,23 +1735,85 @@
<div class="api-header" onclick="toggleDetail(this)"> <div class="api-header" onclick="toggleDetail(this)">
<span class="method GET">GET</span> <span class="method GET">GET</span>
<span class="api-path">/api/movies</span> <span class="api-path">/api/movies</span>
<span class="api-desc">电影列表查询</span><span class="auth-badge">需Token</span> <span class="api-desc">电影列表查询按文件夹分组每组最多10条</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section">
<h4>功能说明</h4>
<p><code>wp_drive_movie</code> 表查询当前用户所有电影,按文件夹分组返回,每个文件夹最多返回 10 条电影数据。支持分页pageNo 控制文件夹分页。</p>
</div>
<div class="detail-section"> <div class="detail-section">
<h4>查询参数</h4> <h4>查询参数</h4>
<table class="param-table"> <table class="param-table">
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr> <tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
<tr><td>keyword</td><td>String</td><td></td><td>搜索关键词</td></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>status</td><td>String</td><td></td><td>电影状态active正常/ deleted已删除</td></tr>
<tr><td>category</td><td>String</td><td></td><td>分类</td></tr>
<tr><td>scrapeType</td><td>String</td><td></td><td>刮削类型tmdb / manual / none</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>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>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>pageNo</td><td>int</td><td></td><td>页码,默认 1</td></tr>
<tr><td>pageSize</td><td>int</td><td></td><td>每页条数,默认 20</td></tr> <tr><td>pageSize</td><td>int</td><td></td><td>每页条数(文件夹数),默认 20</td></tr>
</table> </table>
</div> </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[].subtitle</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[].posterUrl</td><td>String</td><td>竖版海报URL2:3 比例)</td></tr>
<tr><td>folderGroups[].movies[].backdropUrl</td><td>String</td><td>横版背景图URL16: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> </div>
</div> </div>
@ -1643,6 +1826,97 @@
<div class="api-detail"></div> <div class="api-detail"></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>
</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>
}
}</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>404</td><td>电影不存在或无权访问</td></tr>
<tr><td>500</td><td>服务器内部错误</td></tr>
</table>
</div>
</div>
</div>
<div class="api-item"> <div class="api-item">
<div class="api-header" onclick="toggleDetail(this)"> <div class="api-header" onclick="toggleDetail(this)">
<span class="method POST">POST</span> <span class="method POST">POST</span>

View File

@ -296,6 +296,7 @@
<div class="nav-item active" data-section="overview">架构总览</div> <div class="nav-item active" data-section="overview">架构总览</div>
<div class="nav-item" data-section="user">用户服务</div> <div class="nav-item" data-section="user">用户服务</div>
<div class="nav-item" data-section="users">用户管理</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="drive">网盘服务</div>
<div class="nav-item" data-section="icon">Icon服务</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="tmdb">TMDB服务</div>
@ -346,7 +347,10 @@
<tr><th>请求路径</th><th>转发服务</th><th>说明</th></tr> <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/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/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/**</td><td>drive-service</td><td>网盘管理</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/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> <tr><td>/api/tmdb/**, /api/tmdb-scraper/**</td><td>tmdb-service</td><td>TMDB刮削</td></tr>
</table> </table>
@ -665,6 +669,118 @@
</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>
</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>
</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://118.178.238.159:39000/wangpan/avatar/1_1719234567890.png?..."</span>
}
}</div>
</div>
</div>
</div>
</div>
<!-- 网盘服务 --> <!-- 网盘服务 -->
<div class="section" id="drive"> <div class="section" id="drive">
<div class="service-card"> <div class="service-card">
@ -941,7 +1057,7 @@
<div class="api-header" onclick="toggleDetail(this)"> <div class="api-header" onclick="toggleDetail(this)">
<span class="method POST">POST</span> <span class="method POST">POST</span>
<span class="api-path">/api/drive/{driveId}/movies</span> <span class="api-path">/api/drive/{driveId}/movies</span>
<span class="api-desc">上传电影文件</span><span class="auth-badge">需Token</span> <span class="api-desc">创建电影(只传文件名)</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section"> <div class="detail-section">
@ -954,31 +1070,21 @@
<div class="detail-section"> <div class="detail-section">
<h4>请求体</h4> <h4>请求体</h4>
<div class="code-block">{ <div class="code-block">{
<span class="key">"title"</span>: <span class="string">"电影名称"</span>, <span style="color:#718096">// 必填</span> <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> <span class="key">"folderId"</span>: <span class="number">0</span> <span style="color:#718096">// 可选默认0根目录</span>
<span class="key">"subtitle"</span>: <span class="string">"副标题"</span>, <span style="color:#718096">// 可选</span>
<span class="key">"description"</span>: <span class="string">"描述"</span>, <span style="color:#718096">// 可选</span>
<span class="key">"fileData"</span>: <span class="string">"Base64编码的文件数据"</span>, <span style="color:#718096">// 必填</span>
<span class="key">"rating"</span>: <span class="number">85</span>, <span style="color:#718096">// 可选评分0-100</span>
<span class="key">"tags"</span>: <span class="string">"标签1,标签2"</span> <span style="color:#718096">// 可选</span>
}</div> }</div>
</div> </div>
<div class="detail-section"> <div class="detail-section">
<h4>参数说明</h4> <h4>参数说明</h4>
<table class="param-table"> <table class="param-table">
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr> <tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
<tr><td>title</td><td>String</td><td></td><td>电影名称(同时作为文件名</td></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> <tr><td>folderId</td><td>Long</td><td></td><td>目标文件夹ID默认0根目录</td></tr>
<tr><td>subtitle</td><td>String</td><td></td><td>副标题</td></tr>
<tr><td>description</td><td>String</td><td></td><td>描述</td></tr>
<tr><td>fileData</td><td>String</td><td></td><td>Base64编码的视频文件数据</td></tr>
<tr><td>rating</td><td>Integer</td><td></td><td>评分</td></tr>
<tr><td>tags</td><td>String</td><td></td><td>标签</td></tr>
</table> </table>
</div> </div>
<div class="detail-section"> <div class="detail-section">
<h4>说明</h4> <h4>说明</h4>
<p>文件通过 Base64 编码上传,服务端解码后存储到 MinIO返回文件访问 URL。自动检测 MIME 类型mp4/mkv/avi → video/mp4</p> <p>只记录文件名,不上传文件数据。创建后通过 TMDB 刮削接口补充电影的海报、描述、评分等信息。自动检测 MIME 类型mp4/mkv/avi → video/mp4</p>
</div> </div>
</div> </div>
</div> </div>
@ -1088,9 +1194,10 @@
</table> </table>
</div> </div>
<div class="detail-section"> <div class="detail-section">
<h4>请求体(部分更新)</h4> <h4>请求体(部分更新,所有字段可选</h4>
<div class="code-block">{ <div class="code-block">{
<span class="key">"title"</span>: <span class="string">"新电影名称"</span>, <span class="key">"title"</span>: <span class="string">"新电影名称"</span>,
<span class="key">"originalName"</span>: <span class="string">"新文件名.mp4"</span>,
<span class="key">"subtitle"</span>: <span class="string">"新副标题"</span>, <span class="key">"subtitle"</span>: <span class="string">"新副标题"</span>,
<span class="key">"description"</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">"rating"</span>: <span class="number">90</span>,
@ -1098,6 +1205,23 @@
<span class="key">"category"</span>: <span class="string">"新分类"</span> <span class="key">"category"</span>: <span class="string">"新分类"</span>
}</div> }</div>
</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>originalName</td><td>String</td><td>原始文件名(用于 TMDB 刮削有误时修改)</td></tr>
<tr><td>subtitle</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>category</td><td>String</td><td>分类</td></tr>
</table>
</div>
<div class="detail-section">
<h4>说明</h4>
<p>用于 TMDB 刮削有误时,用户可自定义修改文件名等信息。所有字段均为可选,只更新传入的字段。</p>
</div>
</div> </div>
</div> </div>
@ -1463,6 +1587,64 @@
</div> </div>
</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/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 &lt;token&gt;" \
-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://118.178.238.159: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>
<table class="param-table">
<tr><th>code</th><th>说明</th></tr>
<tr><td>400</td><td>文件为空/格式不支持/超过 1MB</td></tr>
<tr><td>401</td><td>未登录或 Token 无效</td></tr>
<tr><td>500</td><td>Icon 不存在或无权访问/上传失败</td></tr>
</table>
</div>
</div>
</div>
</div> </div>
<!-- TMDB服务 --> <!-- TMDB服务 -->
@ -1476,12 +1658,19 @@
<div class="api-header" onclick="toggleDetail(this)"> <div class="api-header" onclick="toggleDetail(this)">
<span class="method GET">GET</span> <span class="method GET">GET</span>
<span class="api-path">/api/movies/recommend</span> <span class="api-path">/api/movies/recommend</span>
<span class="api-desc">今日推荐(随机推荐3部电影</span><span class="auth-badge">需Token</span> <span class="api-desc">今日推荐(每天固定3部电影</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section"> <div class="detail-section">
<h4>功能说明</h4> <h4>功能说明</h4>
<p>从当前用户的电影库中随机抽取3部电影用于首页展示每次请求返回不同的推荐结果。</p> <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>
<div class="detail-section"> <div class="detail-section">
<h4>成功响应示例</h4> <h4>成功响应示例</h4>
@ -1546,21 +1735,85 @@
<div class="api-header" onclick="toggleDetail(this)"> <div class="api-header" onclick="toggleDetail(this)">
<span class="method GET">GET</span> <span class="method GET">GET</span>
<span class="api-path">/api/movies</span> <span class="api-path">/api/movies</span>
<span class="api-desc">电影列表查询</span><span class="auth-badge">需Token</span> <span class="api-desc">电影列表查询按文件夹分组每组最多10条</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section">
<h4>功能说明</h4>
<p><code>wp_drive_movie</code> 表查询当前用户所有电影,按文件夹分组返回,每个文件夹最多返回 10 条电影数据。支持分页pageNo 控制文件夹分页。</p>
</div>
<div class="detail-section"> <div class="detail-section">
<h4>查询参数</h4> <h4>查询参数</h4>
<table class="param-table"> <table class="param-table">
<tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr> <tr><th>参数</th><th>类型</th><th>必填</th><th>说明</th></tr>
<tr><td>keyword</td><td>String</td><td></td><td>搜索关键词</td></tr> <tr><td>keyword</td><td>String</td><td></td><td>搜索关键词</td></tr>
<tr><td>status</td><td>String</td><td></td><td>pending / scraped / failed</td></tr> <tr><td>status</td><td>String</td><td></td><td>电影状态active正常/ deleted已删除</td></tr>
<tr><td>category</td><td>String</td><td></td><td>分类</td></tr> <tr><td>scrapeType</td><td>String</td><td></td><td>刮削类型tmdb / manual / none</td></tr>
<tr><td>scrapeType</td><td>String</td><td></td><td>刮削类型</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>pageNo</td><td>int</td><td></td><td>页码,默认 1</td></tr>
<tr><td>pageSize</td><td>int</td><td></td><td>每页条数,默认 20</td></tr> <tr><td>pageSize</td><td>int</td><td></td><td>每页条数(文件夹数),默认 20</td></tr>
</table> </table>
</div> </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[].subtitle</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[].posterUrl</td><td>String</td><td>竖版海报URL2:3 比例)</td></tr>
<tr><td>folderGroups[].movies[].backdropUrl</td><td>String</td><td>横版背景图URL16: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> </div>
</div> </div>
@ -1573,6 +1826,97 @@
<div class="api-detail"></div> <div class="api-detail"></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>
</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>
}
}</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>404</td><td>电影不存在或无权访问</td></tr>
<tr><td>500</td><td>服务器内部错误</td></tr>
</table>
</div>
</div>
</div>
<div class="api-item"> <div class="api-item">
<div class="api-header" onclick="toggleDetail(this)"> <div class="api-header" onclick="toggleDetail(this)">
<span class="method POST">POST</span> <span class="method POST">POST</span>
@ -1581,8 +1925,19 @@
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section"> <div class="detail-section">
<h4>请求体</h4> <h4>请求体所有字段可选title 必填)</h4>
<div class="code-block">{ <span class="key">"title"</span>: <span class="string">"电影名称"</span> }</div> <div class="code-block">{
<span class="key">"title"</span>: <span class="string">"电影名称"</span>, <span style="color:#718096">// 必填</span>
<span class="key">"subtitle"</span>: <span class="string">"副标题"</span>, <span style="color:#718096">// 可选</span>
<span class="key">"resourceLibrary"</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>
</div> </div>
</div> </div>
@ -1593,7 +1948,30 @@
<span class="api-path">/api/movies/{id}</span> <span class="api-path">/api/movies/{id}</span>
<span class="api-desc">更新电影</span><span class="auth-badge">需Token</span> <span class="api-desc">更新电影</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"></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">"title"</span>: <span class="string">"新电影名称"</span>,
<span class="key">"subtitle"</span>: <span class="string">"新副标题"</span>,
<span class="key">"resourceLibrary"</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>
</div> </div>
<div class="api-item"> <div class="api-item">
@ -1625,7 +2003,23 @@
<span class="api-path">/api/movies/stats</span> <span class="api-path">/api/movies/stats</span>
<span class="api-desc">电影统计</span><span class="auth-badge">需Token</span> <span class="api-desc">电影统计</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"></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>
</div> </div>
<div class="api-item"> <div class="api-item">
@ -1659,9 +2053,25 @@
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section"> <div class="detail-section">
<h4>请求体</h4> <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 class="code-block">{ <span class="key">"movieId"</span>: <span class="number">1</span> }</div>
</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">"rating"</span>: <span class="number">85</span>,
<span class="key">"posterUrl"</span>: <span class="string">"海报URL"</span>
}
}</div>
</div>
</div> </div>
</div> </div>
@ -1673,9 +2083,20 @@
</div> </div>
<div class="api-detail"> <div class="api-detail">
<div class="detail-section"> <div class="detail-section">
<h4>请求体</h4> <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>] }</div> <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>
<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>
@ -1703,7 +2124,20 @@
<span class="api-path">/api/tmdb-scraper/stats</span> <span class="api-path">/api/tmdb-scraper/stats</span>
<span class="api-desc">刮削统计</span><span class="auth-badge">需Token</span> <span class="api-desc">刮削统计</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"></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>
<div class="api-item"> <div class="api-item">
@ -1712,7 +2146,24 @@
<span class="api-path">/api/tmdb-scraper/trigger</span> <span class="api-path">/api/tmdb-scraper/trigger</span>
<span class="api-desc">手动触发定时任务</span><span class="auth-badge">需Token</span> <span class="api-desc">手动触发定时任务</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"></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>
<div class="api-item"> <div class="api-item">
@ -1730,6 +2181,26 @@
<tr><td>pageSize</td><td>20</td><td>每页条数</td></tr> <tr><td>pageSize</td><td>20</td><td>每页条数</td></tr>
</table> </table>
</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">"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">"scanTime"</span>: <span class="string">"2026-06-18T10:00:00"</span>,
<span class="key">"totalMovies"</span>: <span class="number">5</span>,
<span class="key">"successCount"</span>: <span class="number">4</span>,
<span class="key">"failCount"</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> </div>
</div> </div>
@ -1739,7 +2210,27 @@
<span class="api-path">/api/tmdb-scraper/cache/{movieId}</span> <span class="api-path">/api/tmdb-scraper/cache/{movieId}</span>
<span class="api-desc">删除缓存</span><span class="auth-badge">需Token</span> <span class="api-desc">删除缓存</span><span class="auth-badge">需Token</span>
</div> </div>
<div class="api-detail"></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>
</div> </div>

View File

@ -5,6 +5,8 @@ server:
spring: spring:
application: application:
name: wangpan-gateway name: wangpan-gateway
codec:
max-in-memory-size: 10MB
cloud: cloud:
gateway: gateway:
routes: routes:
@ -33,10 +35,15 @@ spring:
# Jasypt 加密配置 # Jasypt 加密配置
jasypt: jasypt:
encryptor: encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD:wangpan-secret-key-2024} password: ${JASYPT_ENCRYPTOR_PASSWORD}
algorithm: PBEWithMD5AndDES 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
logging: logging:
level: level:
com.wangpan: DEBUG com.wangpan: INFO
org.springframework.cloud.gateway: DEBUG org.springframework.cloud.gateway: INFO

View File

@ -3,6 +3,7 @@ package com.wangpan.controller;
import com.wangpan.common.Result; import com.wangpan.common.Result;
import com.wangpan.common.annotation.RequireAuth; import com.wangpan.common.annotation.RequireAuth;
import com.wangpan.common.dal.dataobject.DailyRecommendDO; import com.wangpan.common.dal.dataobject.DailyRecommendDO;
import com.wangpan.common.dal.dataobject.DriveMovieDO;
import com.wangpan.common.dal.repository.DailyRecommendRepository; import com.wangpan.common.dal.repository.DailyRecommendRepository;
import com.wangpan.common.dal.repository.DriveMovieRepository; import com.wangpan.common.dal.repository.DriveMovieRepository;
import com.wangpan.common.dal.repository.MovieRepository; import com.wangpan.common.dal.repository.MovieRepository;
@ -123,6 +124,66 @@ public class MovieController {
return Result.success("查询成功", movieToMap(movie)); return Result.success("查询成功", movieToMap(movie));
} }
/**
* 查询单部电影的刮削状态
* GET /api/movies/{id}/scrape-status
*
* 返回三种状态
* - scraping: 刮削中scrapeStatus = pending scraping
* - failed: 刮削失败scrapeStatus = failed包含失败原因
* - success: 刮削成功scrapeStatus = success
*/
@GetMapping("/{id}/scrape-status")
public Result<?> getScrapeStatus(@PathVariable("id") Long id, HttpServletRequest request) {
User user = getCurrentUser(request);
log.info("用户 [{}] 查询电影 ID={} 的刮削状态", user.getUsername(), id);
// 1. 查询电影信息
DriveMovieDO movieDO = driveMovieRepository.findByIdAsDO(id);
if (movieDO == null) {
return Result.notFound("电影不存在");
}
// 2. 校验用户权限
if (!movieDO.getUserId().equals(user.getId())) {
return Result.notFound("电影不存在或无权访问");
}
// 3. 构建刮削状态响应
Map<String, Object> resultData = new HashMap<>();
resultData.put("movieId", id);
resultData.put("title", movieDO.getTitle());
String scrapeStatus = movieDO.getScrapeStatus();
if ("pending".equals(scrapeStatus) || "scraping".equals(scrapeStatus)) {
// 刮削中
resultData.put("status", "scraping");
resultData.put("message", "正在刮削中,请稍候...");
} else if ("failed".equals(scrapeStatus)) {
// 刮削失败
resultData.put("status", "failed");
resultData.put("message", "刮削失败");
resultData.put("error", movieDO.getScrapeError());
} else if ("success".equals(scrapeStatus)) {
// 刮削成功
resultData.put("status", "success");
resultData.put("message", "刮削成功");
resultData.put("posterUrl", movieDO.getPosterUrl());
resultData.put("backdropUrl", movieDO.getBackdropUrl());
resultData.put("rating", movieDO.getRating());
resultData.put("category", movieDO.getCategory());
resultData.put("tags", movieDO.getTags());
} else {
// 未知状态或未刮削
resultData.put("status", "unknown");
resultData.put("message", "未进行刮削或状态未知");
}
return Result.success("查询成功", resultData);
}
/** /**
* 新增电影 * 新增电影
* POST /api/movies * POST /api/movies

View File

@ -21,7 +21,7 @@ minio:
# Jasypt 加密配置 # Jasypt 加密配置
jasypt: jasypt:
encryptor: encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD:wangpan-secret-key-2024} password: ${JASYPT_ENCRYPTOR_PASSWORD}
algorithm: PBEWITHHMACSHA512ANDAES_256 algorithm: PBEWITHHMACSHA512ANDAES_256
key-obtention-iterations: 1000 key-obtention-iterations: 1000
pool-size: 1 pool-size: 1
@ -31,7 +31,7 @@ jasypt:
logging: logging:
level: level:
com.wangpan: DEBUG com.wangpan: INFO
# MyBatis-Plus 配置 # MyBatis-Plus 配置
mybatis-plus: mybatis-plus:
@ -39,7 +39,6 @@ mybatis-plus:
type-aliases-package: com.wangpan.common.dal.dataobject type-aliases-package: com.wangpan.common.dal.dataobject
configuration: configuration:
map-underscore-to-camel-case: true map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config: global-config:
db-config: db-config:
id-type: auto id-type: auto

View File

@ -14,7 +14,7 @@ spring:
# Jasypt 加密配置 # Jasypt 加密配置
jasypt: jasypt:
encryptor: encryptor:
password: ${JASYPT_ENCRYPTOR_PASSWORD:wangpan-secret-key-2024} password: ${JASYPT_ENCRYPTOR_PASSWORD}
algorithm: PBEWITHHMACSHA512ANDAES_256 algorithm: PBEWITHHMACSHA512ANDAES_256
key-obtention-iterations: 1000 key-obtention-iterations: 1000
pool-size: 1 pool-size: 1
@ -24,7 +24,7 @@ jasypt:
logging: logging:
level: level:
com.wangpan: DEBUG com.wangpan: INFO
# MyBatis-Plus 配置 # MyBatis-Plus 配置
mybatis-plus: mybatis-plus:
@ -32,10 +32,16 @@ mybatis-plus:
type-aliases-package: com.wangpan.common.dal.dataobject type-aliases-package: com.wangpan.common.dal.dataobject
configuration: configuration:
map-underscore-to-camel-case: true map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config: global-config:
db-config: db-config:
id-type: auto id-type: auto
logic-delete-field: isDeleted logic-delete-field: isDeleted
logic-delete-value: 1 logic-delete-value: 1
logic-not-delete-value: 0 logic-not-delete-value: 0
# MinIO 对象存储配置(头像上传用)
minio:
endpoint: ENC(nDpSqjv62odavtIjInqIzcjmWua20QGTKKfKsJlxgCTkWGWj/JJsIAEfsLIzKjpbeAMl/mxVJuPQv8o58b46gQ==)
accessKey: ENC(YM+d/P5KM/QqGFcbbxcZlM+jH0dTgaYlGiYOJ04dIyBX+Z4fu+h05tdylTy0SroK)
secretKey: ENC(BKqQFlceb8mGmAMWbmXFVV1US1Ib+24VApZCci0xyz+xAqL9MQetHJKsrWW1Vib2)
bucketName: wangpan

View File

@ -4,3 +4,4 @@ com\wangpan\service\UserService.class
com\wangpan\UserServiceApplication.class com\wangpan\UserServiceApplication.class
com\wangpan\controller\UserController.class com\wangpan\controller\UserController.class
com\wangpan\controller\AuthController.class com\wangpan\controller\AuthController.class
com\wangpan\controller\ProfileController.class

View File

@ -1,6 +1,7 @@
D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\service\UserService.java D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\service\UserService.java
D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\UserServiceApplication.java D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\UserServiceApplication.java
D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\config\DatabaseInitializer.java D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\config\DatabaseInitializer.java
D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\controller\ProfileController.java
D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\controller\AuthController.java D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\controller\AuthController.java
D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\controller\UserController.java D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\controller\UserController.java
D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\controller\LoginLogController.java D:\java2\wangpan\java\wangpan-user-service\src\main\java\com\wangpan\controller\LoginLogController.java