fix: 校验批量保存响应一致性
This commit is contained in:
@@ -233,6 +233,99 @@ describe('batch-form-model', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps every row when failures repeat the same known row', () => {
|
||||
const rows = [createBatchRow(), createBatchRow()];
|
||||
const failure = {
|
||||
clientKey: rows[0]!.clientKey,
|
||||
rowIndex: 1,
|
||||
errorCode: 'INVALID',
|
||||
message: '第一行失败',
|
||||
};
|
||||
const result = applyBatchResult(rows, {
|
||||
successCount: 0,
|
||||
failureCount: 2,
|
||||
failures: [failure, failure],
|
||||
});
|
||||
const generalError = '批量保存结果无法匹配,请核对后重试';
|
||||
|
||||
expect(result.rows).toEqual(rows);
|
||||
expect(result.rowErrors).toEqual(
|
||||
new Map(rows.map((row) => [row.clientKey, generalError])),
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps every row when unknown failure keys resolve to the same row', () => {
|
||||
const rows = [createBatchRow(), createBatchRow()];
|
||||
const result = applyBatchResult(rows, {
|
||||
successCount: 0,
|
||||
failureCount: 2,
|
||||
failures: [
|
||||
{
|
||||
clientKey: 'unknown-a',
|
||||
rowIndex: 1,
|
||||
errorCode: 'INVALID_A',
|
||||
message: '失败 A',
|
||||
},
|
||||
{
|
||||
clientKey: 'unknown-b',
|
||||
rowIndex: 1,
|
||||
errorCode: 'INVALID_B',
|
||||
message: '失败 B',
|
||||
},
|
||||
],
|
||||
});
|
||||
const generalError = '批量保存结果无法匹配,请核对后重试';
|
||||
|
||||
expect(result.rows).toEqual(rows);
|
||||
expect(result.rowErrors).toEqual(
|
||||
new Map(rows.map((row) => [row.clientKey, generalError])),
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps every row when response counts do not cover all input rows', () => {
|
||||
const rows = [createBatchRow(), createBatchRow()];
|
||||
const result = applyBatchResult(rows, {
|
||||
successCount: 0,
|
||||
failureCount: 1,
|
||||
failures: [
|
||||
{
|
||||
clientKey: rows[0]!.clientKey,
|
||||
rowIndex: 1,
|
||||
errorCode: 'INVALID',
|
||||
message: '第一行失败',
|
||||
},
|
||||
],
|
||||
});
|
||||
const generalError = '批量保存结果无法匹配,请核对后重试';
|
||||
|
||||
expect(result.rows).toEqual(rows);
|
||||
expect(result.rowErrors).toEqual(
|
||||
new Map(rows.map((row) => [row.clientKey, generalError])),
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps every row when failure count differs from failure details', () => {
|
||||
const rows = [createBatchRow(), createBatchRow()];
|
||||
const result = applyBatchResult(rows, {
|
||||
successCount: 0,
|
||||
failureCount: 2,
|
||||
failures: [
|
||||
{
|
||||
clientKey: rows[0]!.clientKey,
|
||||
rowIndex: 1,
|
||||
errorCode: 'INVALID',
|
||||
message: '第一行失败',
|
||||
},
|
||||
],
|
||||
});
|
||||
const generalError = '批量保存结果无法匹配,请核对后重试';
|
||||
|
||||
expect(result.rows).toEqual(rows);
|
||||
expect(result.rowErrors).toEqual(
|
||||
new Map(rows.map((row) => [row.clientKey, generalError])),
|
||||
);
|
||||
});
|
||||
|
||||
it('returns no rows or errors when every row succeeds', () => {
|
||||
const result = applyBatchResult([createBatchRow()], {
|
||||
successCount: 1,
|
||||
|
||||
@@ -81,17 +81,29 @@ export function applyBatchResult(
|
||||
result: LmsSubPackageRelationApi.BatchCreateResult,
|
||||
): { rows: BatchFormRow[]; rowErrors: Map<string, string> } {
|
||||
const generalError = '批量保存结果无法匹配,请核对后重试';
|
||||
const unsafeResult = () => ({
|
||||
rows,
|
||||
rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])),
|
||||
});
|
||||
const rowByClientKey = new Map<string, BatchFormRow>();
|
||||
for (const row of rows) {
|
||||
if (rowByClientKey.has(row.clientKey)) {
|
||||
return {
|
||||
rows,
|
||||
rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])),
|
||||
};
|
||||
return unsafeResult();
|
||||
}
|
||||
rowByClientKey.set(row.clientKey, row);
|
||||
}
|
||||
|
||||
if (
|
||||
!Number.isInteger(result.successCount) ||
|
||||
result.successCount < 0 ||
|
||||
!Number.isInteger(result.failureCount) ||
|
||||
result.failureCount < 0 ||
|
||||
result.successCount + result.failureCount !== rows.length ||
|
||||
result.failureCount !== result.failures.length
|
||||
) {
|
||||
return unsafeResult();
|
||||
}
|
||||
|
||||
if (result.failures.length === 0) {
|
||||
return { rows: [], rowErrors: new Map() };
|
||||
}
|
||||
@@ -105,17 +117,21 @@ export function applyBatchResult(
|
||||
(rowByKey && rowByIndex?.clientKey !== rowByKey.clientKey) ||
|
||||
(!rowByKey && !rowByIndex)
|
||||
) {
|
||||
return {
|
||||
rows,
|
||||
rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])),
|
||||
};
|
||||
return unsafeResult();
|
||||
}
|
||||
|
||||
const matchedRow = rowByKey ?? rowByIndex!;
|
||||
if (failedClientKeys.has(matchedRow.clientKey)) {
|
||||
return unsafeResult();
|
||||
}
|
||||
failedClientKeys.add(matchedRow.clientKey);
|
||||
rowErrors.set(matchedRow.clientKey, failure.message);
|
||||
}
|
||||
|
||||
if (failedClientKeys.size !== result.failureCount) {
|
||||
return unsafeResult();
|
||||
}
|
||||
|
||||
return {
|
||||
rows: rows.filter((row) => failedClientKeys.has(row.clientKey)),
|
||||
rowErrors,
|
||||
|
||||
Reference in New Issue
Block a user