Files
YG-Datasets/easy-dataset-main/app/api/projects/[projectId]/files/[fileId]/ga-pairs/route.js

314 lines
11 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.
import { NextResponse } from 'next/server';
import { getGaPairsByFileId, toggleGaPairActive, saveGaPairs, createGaPairs } from '@/lib/db/ga-pairs';
import { getUploadFileInfoById } from '@/lib/db/upload-files';
import { generateGaPairs } from '@/lib/services/ga/ga-generation';
import logger from '@/lib/util/logger';
import { db } from '@/lib/db/index';
/**
* 生成文件的 GA 对
*/
export async function POST(request, { params }) {
try {
const { projectId, fileId } = params;
const { regenerate = false, appendMode = false, language = '中文' } = await request.json();
// 验证参数
if (!projectId || !fileId) {
return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 });
}
logger.info(`Starting GA pairs generation for project: ${projectId}, file: ${fileId}, appendMode: ${appendMode}`);
// 检查文件是否存在
const file = await getUploadFileInfoById(fileId);
if (!file || file.projectId !== projectId) {
return NextResponse.json({ error: 'File not found or does not belong to the project' }, { status: 404 });
}
// 获取现有的GA对
const existingGaPairs = await getGaPairsByFileId(fileId);
// 如果是追加模式且已有GA对或者不是重新生成且已存在GA对
if (!regenerate && !appendMode && existingGaPairs.length > 0) {
return NextResponse.json({
success: true,
message: 'GA pairs already exist for this file',
data: existingGaPairs
});
}
// 读取文件内容
const fileContent = await getFileContent(projectId, file.fileName);
if (!fileContent) {
return NextResponse.json({ error: 'Failed to read file content' }, { status: 500 });
}
logger.info(`File content loaded successfully, length: ${fileContent.length}`);
// 检查模型配置
try {
const { getActiveModel } = await import('@/lib/services/models');
const activeModel = await getActiveModel(projectId);
if (!activeModel) {
logger.error('No active model configuration found');
return NextResponse.json(
{ error: 'No active AI model configured. Please configure a model in settings first.' },
{ status: 400 }
);
}
logger.info(`Using active model: ${activeModel.provider} - ${activeModel.model}`);
} catch (modelError) {
logger.error('Error checking model configuration:', modelError);
return NextResponse.json(
{ error: 'Failed to load model configuration. Please check your AI model settings.' },
{ status: 500 }
);
}
// 调用 LLM 生成 GA 对
logger.info(`Generating GA pairs for file: ${file.fileName}`);
let generatedGaPairs;
try {
generatedGaPairs = await generateGaPairs(fileContent, projectId, language);
if (!generatedGaPairs || generatedGaPairs.length === 0) {
logger.warn('No GA pairs generated from LLM');
return NextResponse.json(
{
error:
'No GA pairs could be generated from the file content. The content might be too short or not suitable for GA pair generation.'
},
{ status: 400 }
);
}
logger.info(`Successfully generated ${generatedGaPairs.length} GA pairs from LLM`);
} catch (generationError) {
logger.error('GA pairs generation failed:', generationError);
// 现有的错误处理逻辑...
let errorMessage = 'Failed to generate GA pairs';
if (generationError.message.includes('No active model')) {
errorMessage = 'No active AI model available. Please configure and activate a model in settings.';
} else if (generationError.message.includes('API key')) {
errorMessage = 'Invalid API key or model configuration. Please check your AI model settings.';
} else if (generationError.message.includes('rate limit')) {
errorMessage = 'API rate limit exceeded. Please try again later.';
} else {
errorMessage = `AI model error: ${generationError.message}`;
}
return NextResponse.json({ error: errorMessage }, { status: 500 });
}
// 保存到数据库
try {
if (appendMode && existingGaPairs.length > 0) {
// 追加模式只保存新生成的GA对不删除现有的
logger.info(`Appending ${generatedGaPairs.length} new GA pairs to existing ${existingGaPairs.length} pairs`);
// 为新GA对设置正确的pairNumber
const startPairNumber = existingGaPairs.length + 1;
const newGaPairData = generatedGaPairs.map((pair, index) => ({
projectId,
fileId,
pairNumber: startPairNumber + index,
genreTitle: pair.genre?.title || pair.genreTitle || '',
genreDesc: pair.genre?.description || pair.genreDesc || '',
audienceTitle: pair.audience?.title || pair.audienceTitle || '',
audienceDesc: pair.audience?.description || pair.audienceDesc || '',
isActive: true
}));
// 只创建新的GA对不删除现有的
await createGaPairs(newGaPairData);
logger.info('New GA pairs appended to database successfully');
} else {
// 覆盖模式:删除现有的,保存新的
await saveGaPairs(projectId, fileId, generatedGaPairs);
logger.info('GA pairs saved to database successfully');
}
} catch (saveError) {
logger.error('Failed to save GA pairs to database:', saveError);
return NextResponse.json(
{ error: 'Generated GA pairs successfully but failed to save to database' },
{ status: 500 }
);
}
// 获取保存后的所有GA对
const allGaPairs = await getGaPairsByFileId(fileId);
if (appendMode && existingGaPairs.length > 0) {
// 追加模式只返回新生成的GA对
const newGaPairs = allGaPairs.slice(existingGaPairs.length);
logger.info(`Successfully appended ${newGaPairs.length} GA pairs. Total pairs: ${allGaPairs.length}`);
return NextResponse.json({
success: true,
message: `${newGaPairs.length} new GA pairs appended successfully`,
data: newGaPairs,
total: allGaPairs.length
});
} else {
// 覆盖模式返回所有GA对
logger.info(`Successfully generated and saved ${allGaPairs.length} GA pairs for file: ${file.fileName}`);
return NextResponse.json({
success: true,
message: 'GA pairs generated successfully',
data: allGaPairs
});
}
} catch (error) {
logger.error('Unexpected error in GA pairs generation:', error);
return NextResponse.json(
{ error: error.message || 'Unexpected error occurred during GA pairs generation' },
{ status: 500 }
);
}
}
/**
* 获取文件的 GA 对
*/
export async function GET(request, { params }) {
try {
const { projectId, fileId } = params;
if (!projectId || !fileId) {
return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 });
}
const gaPairs = await getGaPairsByFileId(fileId);
return NextResponse.json({
success: true,
data: gaPairs
});
} catch (error) {
console.error('Error getting GA pairs:', String(error));
return NextResponse.json({ error: 'Failed to get GA pairs' }, { status: 500 });
}
}
/**
* 更新/替换文件的所有 GA 对
*/
export async function PUT(request, { params }) {
try {
const { projectId, fileId } = params;
const body = await request.json();
if (!projectId || !fileId) {
return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 });
}
const { updates } = body;
if (!updates || !Array.isArray(updates)) {
return NextResponse.json({ error: 'Updates array is required' }, { status: 400 });
}
logger.info(`Replacing all GA pairs for file ${fileId} with ${updates.length} pairs`);
// 使用数据库事务确保原子性操作
const results = await db.$transaction(async tx => {
// 1. 先删除所有现有的GA对
await tx.gaPairs.deleteMany({
where: { fileId }
});
// 2. 然后创建新的GA对
if (updates.length > 0) {
const gaPairData = updates.map((pair, index) => ({
projectId,
fileId,
pairNumber: index + 1,
genreTitle: pair.genreTitle || pair.genre?.title || pair.genre || '',
genreDesc: pair.genreDesc || pair.genre?.description || '',
audienceTitle: pair.audienceTitle || pair.audience?.title || pair.audience || '',
audienceDesc: pair.audienceDesc || pair.audience?.description || '',
isActive: pair.isActive !== undefined ? pair.isActive : true
}));
// 验证数据
for (const data of gaPairData) {
if (!data.genreTitle || !data.audienceTitle) {
throw new Error(`Invalid GA pair data: missing genre or audience title`);
}
}
await tx.gaPairs.createMany({ data: gaPairData });
}
// 3. 返回新创建的GA对
return await tx.gaPairs.findMany({
where: { fileId },
orderBy: { pairNumber: 'asc' }
});
});
logger.info(`Successfully replaced GA pairs, new count: ${results.length}`);
return NextResponse.json({
success: true,
data: results
});
} catch (error) {
logger.error('Error updating GA pairs:', error);
return NextResponse.json({ error: error.message || 'Failed to update GA pairs' }, { status: 500 });
}
}
/**
* 切换 GA 对激活状态
*/
export async function PATCH(request, { params }) {
try {
const { projectId, fileId } = params;
const body = await request.json();
if (!projectId || !fileId) {
return NextResponse.json({ error: 'Project ID and File ID are required' }, { status: 400 });
}
const { gaPairId, isActive } = body;
if (!gaPairId || typeof isActive !== 'boolean') {
return NextResponse.json({ error: 'GA pair ID and active status are required' }, { status: 400 });
}
const updatedPair = await toggleGaPairActive(gaPairId, isActive);
return NextResponse.json({
success: true,
data: updatedPair
});
} catch (error) {
console.error('Error toggling GA pair active status:', String(error));
return NextResponse.json({ error: 'Failed to toggle GA pair active status' }, { status: 500 });
}
}
// Helper function to read file content
async function getFileContent(projectId, fileName) {
try {
const { getProjectRoot } = await import('@/lib/db/base');
const path = await import('path');
const fs = await import('fs');
const projectRoot = await getProjectRoot();
const filePath = path.join(projectRoot, projectId, 'files', fileName.replace('.pdf', '.md'));
return await fs.promises.readFile(filePath, 'utf8');
} catch (error) {
logger.error('Failed to read file content:', error);
return null;
}
}