Files
suanming/server/routes/download.cjs
patdelphi b0594d5131 feat: Complete AI interpretation system and fix database issues
- Fixed all database connection errors in aiInterpretation.cjs
- Updated better-sqlite3 API calls from callback to sync methods
- Removed AI interpretation buttons from history page
- Added pagination to history page (10 records per page)
- Fixed mobile responsive design for AI interpretation results
- Updated Koyeb deployment configuration to use npm instead of pnpm
- Resolved API limit issues for history records
2025-08-22 15:57:53 +08:00

237 lines
6.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const express = require('express');
const { authenticate } = require('../middleware/auth.cjs');
const { dbManager } = require('../database/index.cjs');
const { generateMarkdown } = require('../services/generators/markdownGenerator.cjs');
const { generatePDF } = require('../services/generators/pdfGenerator.cjs');
const router = express.Router();
/**
* 下载分析结果
* POST /api/download
* 支持格式markdown, pdf, png
*/
router.post('/', authenticate, async (req, res) => {
try {
const { analysisData, analysisType, format, userName } = req.body;
const userId = req.user.id;
// 验证必需参数
if (!analysisData || !analysisType || !format) {
return res.status(400).json({
error: '缺少必需参数',
details: 'analysisData, analysisType, format 都是必需的'
});
}
// 验证格式类型
const supportedFormats = ['markdown', 'pdf', 'png'];
if (!supportedFormats.includes(format)) {
return res.status(400).json({
error: '不支持的格式',
supportedFormats
});
}
// 验证分析类型
const supportedAnalysisTypes = ['bazi', 'ziwei', 'yijing'];
if (!supportedAnalysisTypes.includes(analysisType)) {
return res.status(400).json({
error: '不支持的分析类型',
supportedAnalysisTypes
});
}
let fileBuffer;
let contentType;
let fileExtension;
let filename;
// 生成文件名 - 格式:"分析类型_用户名_日期_时间"(使用分析记录创建时间)
// 优先使用分析记录的创建时间,如果没有则使用当前时间
let analysisDate;
if (analysisData.created_at) {
analysisDate = new Date(analysisData.created_at);
} else if (analysisData.basic_info?.created_at) {
analysisDate = new Date(analysisData.basic_info.created_at);
} else {
// 如果没有创建时间,使用当前时间作为备用
analysisDate = new Date();
}
const year = analysisDate.getFullYear();
const month = String(analysisDate.getMonth() + 1).padStart(2, '0');
const day = String(analysisDate.getDate()).padStart(2, '0');
const hour = String(analysisDate.getHours()).padStart(2, '0');
const minute = String(analysisDate.getMinutes()).padStart(2, '0');
const second = String(analysisDate.getSeconds()).padStart(2, '0');
const dateStr = `${year}${month}${day}`;
const timeStr = `${hour}${minute}${second}`;
// 分析类型映射
const analysisTypeMap = {
'bazi': '八字命理',
'ziwei': '紫微斗数',
'yijing': '易经占卜'
};
const analysisTypeName = analysisTypeMap[analysisType] || analysisType;
const exportMode = '服务器导出';
const baseFilename = `${analysisTypeName}_${userName || 'user'}_${exportMode}_${dateStr}_${timeStr}`;
// 文件名格式: 八字命理_午饭_服务器导出_20250821_133105
try {
switch (format) {
case 'markdown':
fileBuffer = await generateMarkdown(analysisData, analysisType, userName);
contentType = 'text/markdown';
fileExtension = 'md';
filename = `${baseFilename}.md`;
break;
case 'pdf':
fileBuffer = await generatePDF(analysisData, analysisType, userName);
contentType = 'application/pdf';
fileExtension = 'pdf';
filename = `${baseFilename}.pdf`;
break;
}
} catch (generationError) {
console.error(`生成${format}文件失败:`, generationError);
return res.status(500).json({
error: `生成${format}文件失败`,
details: generationError.message
});
}
// 记录下载历史(可选)
try {
const db = dbManager.getDatabase();
const stmt = db.prepare(`
INSERT INTO download_history (user_id, analysis_type, format, filename, created_at)
VALUES (?, ?, ?, ?, datetime('now'))
`);
stmt.run(userId, analysisType, format, filename);
} catch (dbError) {
// 下载历史记录失败不影响文件下载
console.warn('记录下载历史失败:', dbError);
}
// 设置响应头
res.setHeader('Content-Type', contentType);
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(filename)}"`);
res.setHeader('Content-Length', fileBuffer.length);
res.setHeader('Cache-Control', 'no-cache');
// 发送文件
res.send(fileBuffer);
} catch (error) {
console.error('下载API错误:', error);
res.status(500).json({
error: '服务器内部错误',
details: error.message
});
}
});
/**
* 获取用户下载历史
* GET /api/download/history
*/
router.get('/history', authenticate, async (req, res) => {
try {
const userId = req.user.id;
const { page = 1, limit = 20 } = req.query;
const db = dbManager.getDb();
// 获取总数
const countStmt = db.prepare('SELECT COUNT(*) as total FROM download_history WHERE user_id = ?');
const { total } = countStmt.get(userId);
// 获取分页数据
const offset = (page - 1) * limit;
const stmt = db.prepare(`
SELECT analysis_type, format, filename, created_at
FROM download_history
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`);
const downloads = stmt.all(userId, limit, offset);
res.json({
downloads,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
} catch (error) {
console.error('获取下载历史失败:', error);
res.status(500).json({
error: '获取下载历史失败',
details: error.message
});
}
});
/**
* 获取支持的格式和分析类型
* GET /api/download/formats
*/
router.get('/formats', (req, res) => {
res.json({
supportedFormats: [
{
format: 'markdown',
label: 'Markdown文档',
description: '结构化文本格式,便于编辑',
mimeType: 'text/markdown',
extension: 'md'
},
{
format: 'pdf',
label: 'PDF文档',
description: '专业格式,便于打印和分享',
mimeType: 'application/pdf',
extension: 'pdf'
},
{
format: 'png',
label: 'PNG图片',
description: '高清图片格式,便于保存',
mimeType: 'image/png',
extension: 'png'
}
],
supportedAnalysisTypes: [
{
type: 'bazi',
label: '八字命理',
description: '基于传统八字学说的命理分析'
},
{
type: 'ziwei',
label: '紫微斗数',
description: '通过星曜排布分析命运走向'
},
{
type: 'yijing',
label: '易经占卜',
description: '运用梅花易数解读卦象含义'
}
]
});
});
module.exports = router;