import express from 'express'; import persistentImageLinkService from '../services/persistentImageLinkService.js'; import { authenticateToken } from '../middleware/auth.js'; const router = express.Router(); /** * 生成PPT所有页面的持久化链接 * POST /api/persistent-links/generate-all */ router.post('/generate-all', authenticateToken, async (req, res, next) => { try { const { pptId, slides, options = {} } = req.body; const userId = req.user.userId; console.log(`🔗 Generate all persistent links: userId=${userId}, pptId=${pptId}, slides=${slides?.length}`); // 验证参数 if (!pptId) { return res.status(400).json({ error: 'PPT ID is required' }); } if (!slides || !Array.isArray(slides) || slides.length === 0) { return res.status(400).json({ error: 'Slides data is required' }); } // 使用持久化链接服务批量生成链接 const results = await persistentImageLinkService.updateAllPersistentLinksWithFrontendExport( userId, pptId, slides, { format: 'jpg', quality: 0.9, viewportSize: 1000, viewportRatio: 0.5625, ...options } ); // 统计成功和失败的数量 const successCount = results.filter(r => r.success).length; const failureCount = results.filter(r => !r.success).length; console.log(`✅ Persistent links generation completed: ${successCount} success, ${failureCount} failed`); // 返回结果 res.json({ success: true, message: `成功生成 ${successCount} 个持久化链接${failureCount > 0 ? `,${failureCount} 个失败` : ''}`, links: results.filter(r => r.success).map(r => ({ linkId: r.linkId, url: r.url, publicUrl: r.publicUrl, pageIndex: r.pageIndex })), stats: { total: slides.length, success: successCount, failure: failureCount }, errors: results.filter(r => !r.success).map(r => ({ pageIndex: r.pageIndex, error: r.error })) }); } catch (error) { console.error('Generate all persistent links failed:', error); next(error); } }); /** * 获取PPT的所有持久化链接 * GET /api/persistent-links/:pptId */ router.get('/:pptId', authenticateToken, async (req, res, next) => { try { const { pptId } = req.params; const userId = req.user.userId; console.log(`🔍 Get persistent links: userId=${userId}, pptId=${pptId}`); // 获取用户的所有持久化链接 const userLinks = await persistentImageLinkService.getUserLinks(userId); // 过滤出指定PPT的链接 const pptLinks = userLinks.filter(link => link.pptId === pptId); // 按页面索引排序 pptLinks.sort((a, b) => a.pageIndex - b.pageIndex); res.json({ success: true, links: pptLinks.map(link => ({ linkId: link.linkId, url: link.url, publicUrl: link.publicUrl, pageIndex: link.pageIndex, lastUpdated: link.lastUpdated, hasImage: link.hasImage, imageSize: link.imageSize, format: link.format })) }); } catch (error) { console.error('Get persistent links failed:', error); next(error); } }); /** * 更新单个页面的持久化链接 * POST /api/persistent-links/:pptId/:pageIndex/update */ router.post('/:pptId/:pageIndex/update', authenticateToken, async (req, res, next) => { try { const { pptId, pageIndex } = req.params; const { slideData, options = {} } = req.body; const userId = req.user.userId; console.log(`🔄 Update persistent link: userId=${userId}, pptId=${pptId}, pageIndex=${pageIndex}`); // 验证参数 if (!slideData) { return res.status(400).json({ error: 'Slide data is required' }); } const pageIdx = parseInt(pageIndex); if (isNaN(pageIdx) || pageIdx < 0) { return res.status(400).json({ error: 'Invalid page index' }); } // 获取或创建持久化链接 const result = await persistentImageLinkService.getOrCreatePersistentLink( userId, pptId, pageIdx, slideData, { format: 'jpg', quality: 0.9, viewportSize: 1000, viewportRatio: 0.5625, ...options } ); console.log(`✅ Persistent link updated: ${result.linkId}`); res.json({ success: true, linkId: result.linkId, url: result.url, publicUrl: result.publicUrl, pageIndex: pageIdx }); } catch (error) { console.error('Update persistent link failed:', error); next(error); } }); /** * 删除持久化链接 * DELETE /api/persistent-links/:linkId */ router.delete('/:linkId', authenticateToken, async (req, res, next) => { try { const { linkId } = req.params; const userId = req.user.userId; console.log(`🗑️ Delete persistent link: userId=${userId}, linkId=${linkId}`); // 验证链接所有权 const linkInfo = await persistentImageLinkService.getPersistentImage(linkId); if (!linkInfo || linkInfo.userId !== userId) { return res.status(404).json({ error: 'Persistent link not found or access denied' }); } // 删除链接 await persistentImageLinkService.deletePersistentLink(linkId); console.log(`✅ Persistent link deleted: ${linkId}`); res.json({ success: true, message: 'Persistent link deleted successfully' }); } catch (error) { console.error('Delete persistent link failed:', error); next(error); } }); /** * 获取持久化链接服务统计信息 * GET /api/persistent-links/stats */ router.get('/stats', authenticateToken, async (req, res, next) => { try { const userId = req.user.userId; console.log(`📊 Get persistent links stats: userId=${userId}`); // 获取用户的所有链接 const userLinks = await persistentImageLinkService.getUserLinks(userId); // 计算统计信息 const stats = { totalLinks: userLinks.length, linksWithImages: userLinks.filter(link => link.hasImage).length, totalImageSize: userLinks.reduce((sum, link) => sum + (link.imageSize || 0), 0), lastUpdated: userLinks.length > 0 ? Math.max(...userLinks.map(link => new Date(link.lastUpdated || 0).getTime())) : null, pptCount: new Set(userLinks.map(link => link.pptId)).size }; res.json({ success: true, stats: { ...stats, lastUpdated: stats.lastUpdated ? new Date(stats.lastUpdated).toISOString() : null, averageImageSize: stats.linksWithImages > 0 ? Math.round(stats.totalImageSize / stats.linksWithImages) : 0, totalImageSizeMB: Math.round(stats.totalImageSize / 1024 / 1024 * 100) / 100 } }); } catch (error) { console.error('Get persistent links stats failed:', error); next(error); } }); export default router;