Skip to content

Commit a456b47

Browse files
authored
Merge pull request #586 from ConardLi/dev
1.6.0
2 parents 9629465 + 9ec4261 commit a456b47

134 files changed

Lines changed: 13856 additions & 693 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/api/projects/[projectId]/datasets/export/route.js

Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,30 +22,52 @@ export async function GET(request, { params }) {
2222
return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 });
2323
}
2424

25-
let status = searchParams.get('status');
25+
const confirmedParam = searchParams.get('confirmed');
26+
const confirmed = confirmedParam === null ? undefined : confirmedParam === 'true';
27+
28+
// 获取标签统计信息
29+
const tagStats = await getTagsWithDatasetCounts(projectId, confirmed);
30+
return NextResponse.json(tagStats);
31+
} catch (error) {
32+
console.error('Failed to get tag statistics:', String(error));
33+
return NextResponse.json(
34+
{
35+
error: error.message || 'Failed to get tag statistics'
36+
},
37+
{ status: 500 }
38+
);
39+
}
40+
}
41+
42+
/**
43+
* 获取标签统计信息
44+
*/
45+
export async function POST(request, { params }) {
46+
try {
47+
const { projectId } = params;
48+
const body = await request.json();
49+
50+
// 验证项目ID
51+
if (!projectId) {
52+
return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 });
53+
}
54+
55+
let status = body.status;
2656
let confirmed = undefined;
2757
if (status === 'confirmed') confirmed = true;
2858
if (status === 'unconfirmed') confirmed = false;
2959

3060
// 检查是否是分批导出模式
31-
const batchMode = searchParams.get('batchMode');
32-
const offset = parseInt(searchParams.get('offset')) || 0;
33-
const batchSize = parseInt(searchParams.get('batchSize')) || 1000;
61+
const batchMode = body.batchMode ? 'true' : 'false';
62+
const offset = body.offset ?? 0;
63+
const batchSize = body.batchSize ?? 1000;
3464

3565
// 检查是否是平衡导出
36-
const balanceMode = searchParams.get('balanceMode');
37-
const balanceConfig = searchParams.get('balanceConfig');
66+
const balanceMode = body.balanceMode ? 'true' : 'false';
67+
const balanceConfig = body.balanceConfig;
3868

3969
// 检查是否有选中的数据集 ID
40-
const selectedIdsParam = searchParams.get('selectedIds');
41-
let selectedIds = null;
42-
if (selectedIdsParam) {
43-
try {
44-
selectedIds = JSON.parse(selectedIdsParam);
45-
} catch (e) {
46-
console.error('Failed to parse selectedIds:', e);
47-
}
48-
}
70+
const selectedIds = Array.isArray(body.selectedIds) ? body.selectedIds : null;
4971

5072
if (batchMode === 'true') {
5173
// 分批导出模式
@@ -60,7 +82,7 @@ export async function GET(request, { params }) {
6082
});
6183
} else if (balanceMode === 'true' && balanceConfig) {
6284
// 平衡分批导出
63-
const parsedConfig = JSON.parse(balanceConfig);
85+
const parsedConfig = typeof balanceConfig === 'string' ? JSON.parse(balanceConfig) : balanceConfig;
6486
const result = await getBalancedDatasetsByTagsBatch(projectId, parsedConfig, confirmed, offset, batchSize);
6587
return NextResponse.json({
6688
data: result.data,
@@ -78,14 +100,14 @@ export async function GET(request, { params }) {
78100
});
79101
}
80102
} else {
81-
// 传统一次性导出模式(保持向后兄容
103+
// 传统一次性导出模式(保持向后兼容
82104
if (selectedIds && selectedIds.length > 0) {
83105
// 按选中 ID 导出
84106
const datasets = await getDatasetsByIds(projectId, selectedIds);
85107
return NextResponse.json(datasets);
86108
} else if (balanceMode === 'true' && balanceConfig) {
87109
// 平衡导出模式
88-
const parsedConfig = JSON.parse(balanceConfig);
110+
const parsedConfig = typeof balanceConfig === 'string' ? JSON.parse(balanceConfig) : balanceConfig;
89111
const datasets = await getBalancedDatasetsByTags(projectId, parsedConfig, confirmed);
90112
return NextResponse.json(datasets);
91113
} else {
@@ -104,31 +126,3 @@ export async function GET(request, { params }) {
104126
);
105127
}
106128
}
107-
108-
/**
109-
* 获取标签统计信息
110-
*/
111-
export async function POST(request, { params }) {
112-
try {
113-
const { projectId } = params;
114-
const body = await request.json();
115-
const { confirmed } = body;
116-
117-
// 验证项目ID
118-
if (!projectId) {
119-
return NextResponse.json({ error: 'Project ID cannot be empty' }, { status: 400 });
120-
}
121-
122-
// 获取标签统计信息
123-
const tagStats = await getTagsWithDatasetCounts(projectId, confirmed);
124-
return NextResponse.json(tagStats);
125-
} catch (error) {
126-
console.error('Failed to get tag statistics:', String(error));
127-
return NextResponse.json(
128-
{
129-
error: error.message || 'Failed to get tag statistics'
130-
},
131-
{ status: 500 }
132-
);
133-
}
134-
}

app/api/projects/[projectId]/datasets/route.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export async function GET(request, { params }) {
5757
const scoreRange = searchParams.get('scoreRange');
5858
const customTag = searchParams.get('customTag');
5959
const noteKeyword = searchParams.get('noteKeyword');
60+
const chunkName = searchParams.get('chunkName');
6061
let confirmed = undefined;
6162
if (status === 'confirmed') confirmed = true;
6263
if (status === 'unconfirmed') confirmed = false;
@@ -73,7 +74,8 @@ export async function GET(request, { params }) {
7374
isDistill,
7475
scoreRange,
7576
customTag,
76-
noteKeyword
77+
noteKeyword,
78+
chunkName
7779
);
7880
return NextResponse.json(data);
7981
}
@@ -90,7 +92,8 @@ export async function GET(request, { params }) {
9092
isDistill, // 传递蒸馏数据集筛选参数
9193
scoreRange, // 传递评分范围筛选参数
9294
customTag, // 传递自定义标签筛选参数
93-
noteKeyword // 传递备注关键字筛选参数
95+
noteKeyword, // 传递备注关键字筛选参数
96+
chunkName // 传递文本块名称筛选参数
9497
);
9598

9699
return NextResponse.json(datasets);

app/api/projects/[projectId]/distill/questions/by-tag/route.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ export async function GET(request, { params }) {
1010
const { searchParams } = new URL(request.url);
1111
const tagId = searchParams.get('tagId');
1212

13-
console.log('[distill/questions/by-tag] 请求参数:', { projectId, tagId });
14-
1513
// 验证参数
1614
if (!projectId) {
1715
return NextResponse.json({ error: '项目ID不能为空' }, { status: 400 });
@@ -30,8 +28,6 @@ export async function GET(request, { params }) {
3028
return NextResponse.json({ error: '标签不存在' }, { status: 404 });
3129
}
3230

33-
console.log('[distill/questions/by-tag] 标签信息:', tag);
34-
3531
// 获取或创建蒸馏文本块
3632
let distillChunk = await db.chunks.findFirst({
3733
where: {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { NextResponse } from 'next/server';
2+
import { db } from '@/lib/db';
3+
4+
/**
5+
* 更新标签接口
6+
*/
7+
export async function PUT(request, { params }) {
8+
try {
9+
const { projectId, tagId } = params;
10+
11+
// 验证参数
12+
if (!projectId || !tagId) {
13+
return NextResponse.json({ error: '项目ID和标签ID不能为空' }, { status: 400 });
14+
}
15+
16+
const { label } = await request.json();
17+
18+
if (!label || !label.trim()) {
19+
return NextResponse.json({ error: '标签名称不能为空' }, { status: 400 });
20+
}
21+
22+
// 检查标签是否存在
23+
const existingTag = await db.tags.findUnique({
24+
where: { id: tagId }
25+
});
26+
27+
if (!existingTag) {
28+
return NextResponse.json({ error: '标签不存在' }, { status: 404 });
29+
}
30+
31+
// 检查项目ID是否匹配
32+
if (existingTag.projectId !== projectId) {
33+
return NextResponse.json({ error: '无权限编辑此标签' }, { status: 403 });
34+
}
35+
36+
// 检查新标签名称是否已存在(同级标签)
37+
const duplicateTag = await db.tags.findFirst({
38+
where: {
39+
projectId,
40+
label: label.trim(),
41+
parentId: existingTag.parentId,
42+
id: { not: tagId }
43+
}
44+
});
45+
46+
if (duplicateTag) {
47+
return NextResponse.json({ error: '同级标签名称已存在' }, { status: 400 });
48+
}
49+
50+
// 更新标签
51+
const updatedTag = await db.tags.update({
52+
where: { id: tagId },
53+
data: { label: label.trim() }
54+
});
55+
56+
return NextResponse.json(updatedTag);
57+
} catch (error) {
58+
console.error('[标签编辑] 更新标签失败:', String(error));
59+
return NextResponse.json({ error: error.message || '更新标签失败' }, { status: 500 });
60+
}
61+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { NextResponse } from 'next/server';
2+
import { getImageDatasetById, updateImageDataset, deleteImageDataset } from '@/lib/db/imageDatasets';
3+
import { getProjectPath } from '@/lib/db/base';
4+
import fs from 'fs/promises';
5+
import path from 'path';
6+
7+
// 获取单个数据集详情
8+
export async function GET(request, { params }) {
9+
try {
10+
const { projectId, datasetId } = params;
11+
12+
const dataset = await getImageDatasetById(datasetId);
13+
14+
if (!dataset || dataset.projectId !== projectId) {
15+
return NextResponse.json({ error: 'Dataset not found' }, { status: 404 });
16+
}
17+
18+
// 获取项目路径
19+
const projectPath = await getProjectPath(projectId);
20+
21+
// 读取图片 base64
22+
let base64 = null;
23+
try {
24+
const imagePath = path.join(projectPath, 'images', dataset.imageName);
25+
const imageBuffer = await fs.readFile(imagePath);
26+
const base64Data = imageBuffer.toString('base64');
27+
const ext = path.extname(dataset.imageName).toLowerCase();
28+
const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg';
29+
base64 = `data:${mimeType};base64,${base64Data}`;
30+
} catch (error) {
31+
console.error(`Failed to read image ${dataset.imageName}:`, error);
32+
}
33+
34+
// 添加图片 base64
35+
const datasetWithImage = {
36+
...dataset,
37+
base64
38+
};
39+
40+
return NextResponse.json(datasetWithImage);
41+
} catch (error) {
42+
console.error('Failed to get dataset detail:', error);
43+
return NextResponse.json({ error: error.message || 'Failed to get dataset detail' }, { status: 500 });
44+
}
45+
}
46+
47+
// 更新数据集
48+
export async function PUT(request, { params }) {
49+
try {
50+
const { projectId, datasetId } = params;
51+
const updates = await request.json();
52+
53+
// 验证数据集存在且属于该项目
54+
const dataset = await getImageDatasetById(datasetId);
55+
if (!dataset || dataset.projectId !== projectId) {
56+
return NextResponse.json({ error: 'Dataset not found' }, { status: 404 });
57+
}
58+
59+
// 更新数据集
60+
const updated = await updateImageDataset(datasetId, updates);
61+
62+
// 获取项目路径
63+
const projectPath = await getProjectPath(projectId);
64+
65+
// 读取图片 base64
66+
let base64 = null;
67+
try {
68+
const imagePath = path.join(projectPath, 'images', updated.imageName);
69+
const imageBuffer = await fs.readFile(imagePath);
70+
const base64Data = imageBuffer.toString('base64');
71+
const ext = path.extname(updated.imageName).toLowerCase();
72+
const mimeType = ext === '.png' ? 'image/png' : ext === '.gif' ? 'image/gif' : 'image/jpeg';
73+
base64 = `data:${mimeType};base64,${base64Data}`;
74+
} catch (error) {
75+
console.error(`Failed to read image ${updated.imageName}:`, error);
76+
}
77+
78+
// 添加图片 base64
79+
const updatedWithImage = {
80+
...updated,
81+
base64
82+
};
83+
84+
return NextResponse.json(updatedWithImage);
85+
} catch (error) {
86+
console.error('Failed to update dataset:', error);
87+
return NextResponse.json({ error: error.message || 'Failed to update dataset' }, { status: 500 });
88+
}
89+
}
90+
91+
// 删除数据集
92+
export async function DELETE(request, { params }) {
93+
try {
94+
const { projectId, datasetId } = params;
95+
96+
// 验证数据集存在且属于该项目
97+
const dataset = await getImageDatasetById(datasetId);
98+
if (!dataset || dataset.projectId !== projectId) {
99+
return NextResponse.json({ error: 'Dataset not found' }, { status: 404 });
100+
}
101+
102+
await deleteImageDataset(datasetId);
103+
104+
return NextResponse.json({ success: true });
105+
} catch (error) {
106+
console.error('Failed to delete dataset:', error);
107+
return NextResponse.json({ error: error.message || 'Failed to delete dataset' }, { status: 500 });
108+
}
109+
}

0 commit comments

Comments
 (0)