fix: 修复报备导入映射与审核跳转
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
hasValue,
|
||||
normalizeImageExtension,
|
||||
imageContentType,
|
||||
duplicateCoreMappingKind,
|
||||
} from './report-materials.helpers';
|
||||
import { ReportImportParserService } from './import-parser.service';
|
||||
import { compatibleImages, loadCompatibleWorkbook } from './workbook-compatibility';
|
||||
@@ -36,6 +37,17 @@ export class ReportImportReviewService {
|
||||
if (!batch) throw new NotFoundException('导入批次不存在');
|
||||
if (batch.status !== 'analyzed') throw new ConflictException('该导入批次已提交审核,不能重复导入');
|
||||
if (!data.mappings?.length) throw new BadRequestException('请至少配置一个导入字段映射');
|
||||
const duplicateCoreKind = duplicateCoreMappingKind(data.mappings);
|
||||
if (duplicateCoreKind) {
|
||||
const label = {
|
||||
signatureName: '短信签名',
|
||||
purpose: '签名用途/依据',
|
||||
siteName: '站点名称',
|
||||
url: '引流 URL 或号码',
|
||||
remark: '备注',
|
||||
}[duplicateCoreKind];
|
||||
throw new BadRequestException(`目标字段“${label}”只能映射一个源列`);
|
||||
}
|
||||
if (data.profile)
|
||||
await this.importParser.saveImportProfile({
|
||||
...data.profile,
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { ReportMaterialsService } from './report-materials.service';
|
||||
import { duplicateCoreMappingKind, signatureCoreMapping } from './report-materials.helpers';
|
||||
|
||||
describe('report material import mappings', () => {
|
||||
it('distinguishes signature category or purpose columns from the signature name', () => {
|
||||
expect(signatureCoreMapping('签名/签名')).toMatchObject({
|
||||
kind: 'signatureName',
|
||||
code: 'signature_name',
|
||||
});
|
||||
expect(signatureCoreMapping('签名用途/签名用途')).toMatchObject({
|
||||
kind: 'purpose',
|
||||
code: 'purpose',
|
||||
});
|
||||
expect(signatureCoreMapping('签名类别1营业执照2商标3APP/签名类别')).toMatchObject({
|
||||
kind: 'purpose',
|
||||
code: 'purpose',
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects duplicate core targets without blocking distinct dynamic fields', () => {
|
||||
expect(
|
||||
duplicateCoreMappingKind([
|
||||
{
|
||||
sourceHeader: '签名',
|
||||
sourceColumnIndex: 1,
|
||||
targetFieldCode: 'signature_name',
|
||||
targetKind: 'signatureName',
|
||||
fieldType: 'string',
|
||||
},
|
||||
{
|
||||
sourceHeader: '签名类别',
|
||||
sourceColumnIndex: 2,
|
||||
targetFieldCode: 'signature_name',
|
||||
targetKind: 'signatureName',
|
||||
fieldType: 'string',
|
||||
},
|
||||
]),
|
||||
).toBe('signatureName');
|
||||
expect(
|
||||
duplicateCoreMappingKind([
|
||||
{
|
||||
sourceHeader: '正面',
|
||||
sourceColumnIndex: 1,
|
||||
targetFieldCode: 'identity',
|
||||
targetKind: 'dynamic',
|
||||
fieldType: 'image',
|
||||
},
|
||||
{
|
||||
sourceHeader: '反面',
|
||||
sourceColumnIndex: 2,
|
||||
targetFieldCode: 'identity',
|
||||
targetKind: 'dynamic',
|
||||
fieldType: 'image',
|
||||
},
|
||||
]),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('rejects duplicate core mappings before loading the workbook', async () => {
|
||||
const prisma = {
|
||||
reportMaterialImportBatch: {
|
||||
findUnique: jest.fn().mockResolvedValue({ id: 'batch-duplicate', status: 'analyzed' }),
|
||||
},
|
||||
};
|
||||
const files = { getDownload: jest.fn() };
|
||||
const service = new ReportMaterialsService(prisma as never, files as never, {} as never);
|
||||
|
||||
await expect(
|
||||
service.commitImport('batch-duplicate', {
|
||||
mappings: [
|
||||
{
|
||||
sourceHeader: '签名',
|
||||
sourceColumnIndex: 1,
|
||||
targetFieldCode: 'signature_name',
|
||||
targetKind: 'signatureName',
|
||||
fieldType: 'string',
|
||||
},
|
||||
{
|
||||
sourceHeader: '签名类别',
|
||||
sourceColumnIndex: 2,
|
||||
targetFieldCode: 'signature_name',
|
||||
targetKind: 'signatureName',
|
||||
fieldType: 'string',
|
||||
},
|
||||
],
|
||||
}),
|
||||
).rejects.toThrow('目标字段“短信签名”只能映射一个源列');
|
||||
expect(files.getDownload).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -100,6 +100,16 @@ export function suggestMappings(
|
||||
});
|
||||
}
|
||||
|
||||
export function duplicateCoreMappingKind(mappings: ImportMapping[]) {
|
||||
const seen = new Set<ImportMapping['targetKind']>();
|
||||
for (const mapping of mappings) {
|
||||
if (mapping.targetKind === 'dynamic') continue;
|
||||
if (seen.has(mapping.targetKind)) return mapping.targetKind;
|
||||
seen.add(mapping.targetKind);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function remapProfileColumns(
|
||||
profileColumns: ImportMapping[],
|
||||
sourceColumns: Array<{
|
||||
@@ -137,8 +147,8 @@ export function remapProfileColumns(
|
||||
export function signatureCoreMapping(
|
||||
header: string,
|
||||
): { code: string; kind: ImportMapping['targetKind']; required?: boolean } | undefined {
|
||||
if (/签名(?:用途|依据|类别|类型)|用途/.test(header)) return { code: 'purpose', kind: 'purpose' };
|
||||
if (/短信签名|签名名称|签名/.test(header)) return { code: 'signature_name', kind: 'signatureName', required: true };
|
||||
if (/用途|签名依据/.test(header)) return { code: 'purpose', kind: 'purpose' };
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user