feat: add tenant import console

This commit is contained in:
Codex
2026-06-29 13:35:34 +08:00
parent 65905aaf8f
commit 93ec1a07d2
13 changed files with 623 additions and 30 deletions

View File

@@ -3173,6 +3173,55 @@ async function testTenantContentAssetsAndImports() {
assert.equal(csvQuestionPreview.job?.validCount, 1, 'CSV question preview should normalize one row');
assert.equal(csvQuestionPreview.items?.[0]?.normalized?.correctOptionIndices?.[0], 1, 'CSV answer B should map to option index 1');
const mappedQuestionCsv = [
['旧编号', '题型显示', '自定义题干', '左选项', '右选项', '正确项', '讲解'].join(','),
['integration-import-csv-mapped-001', 'choice', 'CSV字段映射题导入别名应由谁最终校验', '前端页面', '后端导入管线', 'B', '字段映射只能进入后端白名单字段。'].join(','),
].join('\n');
const mappedCsvPreview = await request('/api/tenant-content/imports/preview/questions', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',
body: {
sourceFormat: 'csv',
sourceName: 'questions-mapped.csv',
csvText: mappedQuestionCsv,
subjectId: ids.subject,
categoryId: ids.category,
regionId: ids.region,
entryId: ids.contentEntry,
contentNodeId: ids.contentNodeSchoolTarget,
collectionId: ids.questionCollection,
fieldMappingOverrides: {
legacyId: ['旧编号'],
type: ['题型显示'],
content: ['自定义题干'],
optionA: ['左选项'],
optionB: ['右选项'],
answer: ['正确项'],
explanation: ['讲解'],
},
},
});
assert.equal(mappedCsvPreview.job?.errorCount, 0, 'CSV field mapping override preview should have no errors');
assert.equal(mappedCsvPreview.items?.[0]?.normalized?.content, 'CSV字段映射题导入别名应由谁最终校验', 'field mapping override should map custom stem header');
assert.equal(mappedCsvPreview.items?.[0]?.normalized?.correctOptionIndices?.[0], 1, 'field mapping override should map custom answer header');
const unsafeMappingOverrides = Object.fromEntries([['__proto__', ['旧编号']]]);
const deniedMappingTarget = await request('/api/tenant-content/imports/preview/questions', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',
body: {
sourceFormat: 'csv',
sourceName: 'questions-bad-mapping.csv',
csvText: mappedQuestionCsv,
subjectId: ids.subject,
categoryId: ids.category,
regionId: ids.region,
fieldMappingOverrides: unsafeMappingOverrides,
},
expectStatus: 400,
});
assert.equal(deniedMappingTarget.code, 'IMPORT_HEADER_UNSAFE', 'field mapping override should reject unsafe target fields');
const csvQuestionImport = await request('/api/tenant-content/imports/questions', {
userId: TENANT_ADMIN_USER_ID,
method: 'POST',