web_ppt_7.7 / backend /src /routes /persistentLinks.js
CatPtain's picture
Upload 85 files
28e1dba verified
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;