移动端样式优化,修复当下载文件太多时,下载注册扫描前端显示超时问题
This commit is contained in:
+117
-18
@@ -972,6 +972,28 @@ router.get('/stats', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取下载注册表统计信息
|
||||
* GET /api/download/registry/stats
|
||||
*/
|
||||
router.get('/registry/stats', async (req, res) => {
|
||||
try {
|
||||
const downloadService = req.backend.getDownloadService();
|
||||
const stats = await downloadService.downloadRegistry.getStats();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: stats
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('获取下载注册表统计信息失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 导出下载注册表
|
||||
* GET /api/download/registry/export
|
||||
@@ -1032,14 +1054,63 @@ router.post('/registry/rebuild', async (req, res) => {
|
||||
try {
|
||||
const downloadService = req.backend.getDownloadService();
|
||||
const fileManager = downloadService.fileManager;
|
||||
const result = await downloadService.downloadRegistry.rebuildFromFileSystem(fileManager);
|
||||
|
||||
// 生成任务ID
|
||||
const taskId = `registry-rebuild-${Date.now()}`;
|
||||
|
||||
// 立即返回任务ID,不等待完成
|
||||
res.json({
|
||||
success: true,
|
||||
data: result
|
||||
data: {
|
||||
taskId,
|
||||
status: 'started',
|
||||
message: '注册表重建任务已启动'
|
||||
}
|
||||
});
|
||||
|
||||
// 异步执行重建任务
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
// 设置任务状态为进行中
|
||||
global.registryRebuildTasks = global.registryRebuildTasks || new Map();
|
||||
global.registryRebuildTasks.set(taskId, {
|
||||
status: 'running',
|
||||
startTime: Date.now(),
|
||||
progress: {
|
||||
scannedArtists: 0,
|
||||
scannedArtworks: 0,
|
||||
addedArtworks: 0,
|
||||
skippedArtworks: 0,
|
||||
currentArtist: null
|
||||
}
|
||||
});
|
||||
|
||||
const result = await downloadService.downloadRegistry.rebuildFromFileSystem(fileManager, taskId);
|
||||
|
||||
// 更新任务状态为完成
|
||||
global.registryRebuildTasks.set(taskId, {
|
||||
status: 'completed',
|
||||
startTime: global.registryRebuildTasks.get(taskId).startTime,
|
||||
endTime: Date.now(),
|
||||
result: result
|
||||
});
|
||||
|
||||
logger.info(`注册表重建任务完成: ${taskId}`, result);
|
||||
} catch (error) {
|
||||
logger.error(`注册表重建任务失败: ${taskId}`, error);
|
||||
|
||||
// 更新任务状态为失败
|
||||
global.registryRebuildTasks.set(taskId, {
|
||||
status: 'failed',
|
||||
startTime: global.registryRebuildTasks.get(taskId).startTime,
|
||||
endTime: Date.now(),
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('重建下载注册表失败:', error);
|
||||
logger.error('启动注册表重建任务失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
@@ -1048,20 +1119,29 @@ router.post('/registry/rebuild', async (req, res) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取下载注册表统计信息
|
||||
* GET /api/download/registry/stats
|
||||
* 获取注册表重建任务状态
|
||||
* GET /api/download/registry/rebuild/status/:taskId
|
||||
*/
|
||||
router.get('/registry/stats', async (req, res) => {
|
||||
router.get('/registry/rebuild/status/:taskId', async (req, res) => {
|
||||
try {
|
||||
const downloadService = req.backend.getDownloadService();
|
||||
const stats = await downloadService.downloadRegistry.getStats();
|
||||
const { taskId } = req.params;
|
||||
|
||||
global.registryRebuildTasks = global.registryRebuildTasks || new Map();
|
||||
const task = global.registryRebuildTasks.get(taskId);
|
||||
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: '任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: stats
|
||||
data: task
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('获取下载注册表统计信息失败:', error);
|
||||
logger.error('获取注册表重建任务状态失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
@@ -1070,21 +1150,40 @@ router.get('/registry/stats', async (req, res) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* 清理下载注册表
|
||||
* POST /api/download/registry/cleanup
|
||||
* 取消注册表重建任务
|
||||
* DELETE /api/download/registry/rebuild/:taskId
|
||||
*/
|
||||
router.post('/registry/cleanup', async (req, res) => {
|
||||
router.delete('/registry/rebuild/:taskId', async (req, res) => {
|
||||
try {
|
||||
const downloadService = req.backend.getDownloadService();
|
||||
const fileManager = downloadService.fileManager;
|
||||
const result = await downloadService.downloadRegistry.cleanupRegistry(fileManager);
|
||||
const { taskId } = req.params;
|
||||
|
||||
global.registryRebuildTasks = global.registryRebuildTasks || new Map();
|
||||
const task = global.registryRebuildTasks.get(taskId);
|
||||
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
error: '任务不存在'
|
||||
});
|
||||
}
|
||||
|
||||
if (task.status === 'running') {
|
||||
// 标记任务为已取消
|
||||
global.registryRebuildTasks.set(taskId, {
|
||||
...task,
|
||||
status: 'cancelled',
|
||||
endTime: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: result
|
||||
data: {
|
||||
message: '任务已取消'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('清理下载注册表失败:', error);
|
||||
logger.error('取消注册表重建任务失败:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
|
||||
@@ -329,118 +329,95 @@ class DownloadRegistry {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件系统扫描并重建注册表
|
||||
* @param {Object} fileManager - 文件管理器实例
|
||||
* @returns {Object} 扫描结果统计
|
||||
* 从文件系统重建注册表
|
||||
* @param {FileManager} fileManager
|
||||
* @param {string} taskId - 任务ID,用于更新进度
|
||||
* @returns {Promise<{scannedArtists: number, scannedArtworks: number, addedArtworks: number, skippedArtworks: number}>}
|
||||
*/
|
||||
async rebuildFromFileSystem(fileManager) {
|
||||
try {
|
||||
logger.info('开始从文件系统扫描并添加新作品到注册表...');
|
||||
|
||||
if (!this.loaded) {
|
||||
await this.loadRegistry();
|
||||
}
|
||||
|
||||
let scannedArtists = 0;
|
||||
let scannedArtworks = 0;
|
||||
let addedArtworks = 0;
|
||||
let skippedArtworks = 0;
|
||||
async rebuildFromFileSystem(fileManager, taskId = null) {
|
||||
logger.info('开始从文件系统重建下载注册表...');
|
||||
|
||||
const stats = {
|
||||
scannedArtists: 0,
|
||||
scannedArtworks: 0,
|
||||
addedArtworks: 0,
|
||||
skippedArtworks: 0
|
||||
};
|
||||
|
||||
const downloadPath = await fileManager.getDownloadPath();
|
||||
logger.debug(`扫描下载路径: ${downloadPath}`);
|
||||
|
||||
const artists = await fileManager.listDirectory(downloadPath);
|
||||
logger.debug(`找到 ${artists.length} 个作者目录`);
|
||||
// 获取所有艺术家目录
|
||||
const artistDirs = await fileManager.getArtistDirectories();
|
||||
logger.info(`发现 ${artistDirs.length} 个艺术家目录`);
|
||||
|
||||
for (const artist of artists) {
|
||||
try {
|
||||
const artistPath = path.join(downloadPath, artist);
|
||||
const artistStat = await fileManager.getFileInfo(artistPath);
|
||||
|
||||
if (artistStat.exists && artistStat.isDirectory) {
|
||||
scannedArtists++;
|
||||
logger.debug(`扫描作者: ${artist}`);
|
||||
|
||||
const artworks = await fileManager.listDirectory(artistPath);
|
||||
|
||||
for (const artwork of artworks) {
|
||||
try {
|
||||
const artworkPath = path.join(artistPath, artwork);
|
||||
const artworkStat = await fileManager.getFileInfo(artworkPath);
|
||||
|
||||
if (artworkStat.exists && artworkStat.isDirectory) {
|
||||
scannedArtworks++;
|
||||
|
||||
// 检查是否是作品目录(包含数字ID)
|
||||
const artworkMatch = artwork.match(/^(\d+)_(.+)$/);
|
||||
if (artworkMatch) {
|
||||
const artworkId = parseInt(artworkMatch[1]);
|
||||
|
||||
// 检查作品是否已经在注册表中
|
||||
const isAlreadyRegistered = await this.isArtworkDownloaded(artworkId);
|
||||
if (isAlreadyRegistered) {
|
||||
skippedArtworks++;
|
||||
continue; // 跳过已注册的作品
|
||||
}
|
||||
|
||||
// 检查作品信息文件和图片文件
|
||||
const infoPath = path.join(artworkPath, 'artwork_info.json');
|
||||
let artworkInfo;
|
||||
try {
|
||||
const infoContent = await fs.readFile(infoPath, 'utf8');
|
||||
artworkInfo = JSON.parse(infoContent);
|
||||
} catch (error) {
|
||||
logger.debug(`读取作品信息文件失败: ${infoPath}`, error);
|
||||
continue; // 跳过没有信息文件的目录
|
||||
}
|
||||
|
||||
// 检查是否有图片文件
|
||||
const files = await fileManager.listDirectory(artworkPath);
|
||||
const imageFiles = files.filter(file => /\.(jpg|jpeg|png|gif|webp)$/i.test(file));
|
||||
|
||||
if (imageFiles.length > 0) {
|
||||
// 检查图片数量是否与artwork_info.json中记录的一致
|
||||
const expectedImageCount = artworkInfo.page_count || 1;
|
||||
if (imageFiles.length >= expectedImageCount) {
|
||||
// 添加到注册表(只添加新的)
|
||||
await this.addArtwork(artist, artworkId);
|
||||
addedArtworks++;
|
||||
logger.debug(`添加作品到注册表: ${artist} - ${artworkId}`);
|
||||
} else {
|
||||
logger.debug(`作品图片数量不足: ${artist} - ${artworkId}, 期望: ${expectedImageCount}, 实际: ${imageFiles.length}`);
|
||||
}
|
||||
} else {
|
||||
logger.debug(`作品目录无图片文件: ${artworkPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.debug(`处理作品目录 ${artwork} 时出错:`, error);
|
||||
continue; // 跳过有问题的作品目录
|
||||
}
|
||||
// 更新进度的辅助函数
|
||||
const updateProgress = (currentArtist = null) => {
|
||||
if (taskId && global.registryRebuildTasks) {
|
||||
const task = global.registryRebuildTasks.get(taskId);
|
||||
if (task && task.status === 'running') {
|
||||
global.registryRebuildTasks.set(taskId, {
|
||||
...task,
|
||||
progress: {
|
||||
...stats,
|
||||
currentArtist
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.debug(`处理作者目录 ${artist} 时出错:`, error);
|
||||
continue; // 跳过有问题的作者目录
|
||||
});
|
||||
}
|
||||
// 检查是否被取消
|
||||
if (task && task.status === 'cancelled') {
|
||||
throw new Error('任务已被取消');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const result = {
|
||||
scannedArtists,
|
||||
scannedArtworks,
|
||||
addedArtworks,
|
||||
skippedArtworks,
|
||||
totalRegisteredArtists: Object.keys(this.registry.artists).length,
|
||||
totalRegisteredArtworks: this.getTotalArtworkCount()
|
||||
};
|
||||
|
||||
logger.info('注册表扫描完成', result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error('注册表扫描失败:', error);
|
||||
throw error;
|
||||
for (const artistDir of artistDirs) {
|
||||
try {
|
||||
stats.scannedArtists++;
|
||||
updateProgress(artistDir);
|
||||
|
||||
logger.info(`扫描艺术家目录: ${artistDir}`);
|
||||
|
||||
// 获取艺术家目录下的所有作品目录
|
||||
const artworkDirs = await fileManager.getArtworkDirectories(artistDir);
|
||||
|
||||
for (const artworkDir of artworkDirs) {
|
||||
try {
|
||||
stats.scannedArtworks++;
|
||||
updateProgress(artistDir);
|
||||
|
||||
// 检查作品是否已在注册表中
|
||||
const isRegistered = await this.isArtworkRegistered(artistDir, artworkDir);
|
||||
|
||||
if (!isRegistered) {
|
||||
// 获取作品信息并添加到注册表
|
||||
const artworkInfo = await fileManager.getArtworkInfo(artistDir, artworkDir);
|
||||
if (artworkInfo) {
|
||||
await this.addArtwork(artistDir, artworkDir, artworkInfo);
|
||||
stats.addedArtworks++;
|
||||
logger.debug(`添加作品到注册表: ${artistDir}/${artworkDir}`);
|
||||
}
|
||||
} else {
|
||||
stats.skippedArtworks++;
|
||||
}
|
||||
|
||||
// 每处理10个作品更新一次进度
|
||||
if (stats.scannedArtworks % 10 === 0) {
|
||||
updateProgress(artistDir);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`处理作品目录失败 ${artistDir}/${artworkDir}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.warn(`处理艺术家目录失败 ${artistDir}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 最终更新进度
|
||||
updateProgress(null);
|
||||
|
||||
logger.info('从文件系统重建下载注册表完成', stats);
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -639,6 +639,64 @@ class FileManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有艺术家目录
|
||||
*/
|
||||
async getArtistDirectories() {
|
||||
try {
|
||||
const downloadPath = await this.getDownloadPath();
|
||||
if (!await this.directoryExists(downloadPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const items = await this.listDirectory(downloadPath);
|
||||
const artistDirs = [];
|
||||
|
||||
for (const item of items) {
|
||||
const itemPath = path.join(downloadPath, item);
|
||||
const stat = await fs.stat(itemPath);
|
||||
if (stat.isDirectory()) {
|
||||
artistDirs.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return artistDirs;
|
||||
} catch (error) {
|
||||
logger.error('获取艺术家目录失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定艺术家目录下的所有作品目录
|
||||
*/
|
||||
async getArtworkDirectories(artistName) {
|
||||
try {
|
||||
const downloadPath = await this.getDownloadPath();
|
||||
const artistPath = path.join(downloadPath, artistName);
|
||||
|
||||
if (!await this.directoryExists(artistPath)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const items = await this.listDirectory(artistPath);
|
||||
const artworkDirs = [];
|
||||
|
||||
for (const item of items) {
|
||||
const itemPath = path.join(artistPath, item);
|
||||
const stat = await fs.stat(itemPath);
|
||||
if (stat.isDirectory()) {
|
||||
artworkDirs.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
return artworkDirs;
|
||||
} catch (error) {
|
||||
logger.error(`获取艺术家 ${artistName} 的作品目录失败:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查目录是否存在
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user