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', () => {
|
it('returns no rows or errors when every row succeeds', () => {
|
||||||
const result = applyBatchResult([createBatchRow()], {
|
const result = applyBatchResult([createBatchRow()], {
|
||||||
successCount: 1,
|
successCount: 1,
|
||||||
|
|||||||
@@ -81,17 +81,29 @@ export function applyBatchResult(
|
|||||||
result: LmsSubPackageRelationApi.BatchCreateResult,
|
result: LmsSubPackageRelationApi.BatchCreateResult,
|
||||||
): { rows: BatchFormRow[]; rowErrors: Map<string, string> } {
|
): { rows: BatchFormRow[]; rowErrors: Map<string, string> } {
|
||||||
const generalError = '批量保存结果无法匹配,请核对后重试';
|
const generalError = '批量保存结果无法匹配,请核对后重试';
|
||||||
|
const unsafeResult = () => ({
|
||||||
|
rows,
|
||||||
|
rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])),
|
||||||
|
});
|
||||||
const rowByClientKey = new Map<string, BatchFormRow>();
|
const rowByClientKey = new Map<string, BatchFormRow>();
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
if (rowByClientKey.has(row.clientKey)) {
|
if (rowByClientKey.has(row.clientKey)) {
|
||||||
return {
|
return unsafeResult();
|
||||||
rows,
|
|
||||||
rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
rowByClientKey.set(row.clientKey, row);
|
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) {
|
if (result.failures.length === 0) {
|
||||||
return { rows: [], rowErrors: new Map() };
|
return { rows: [], rowErrors: new Map() };
|
||||||
}
|
}
|
||||||
@@ -105,17 +117,21 @@ export function applyBatchResult(
|
|||||||
(rowByKey && rowByIndex?.clientKey !== rowByKey.clientKey) ||
|
(rowByKey && rowByIndex?.clientKey !== rowByKey.clientKey) ||
|
||||||
(!rowByKey && !rowByIndex)
|
(!rowByKey && !rowByIndex)
|
||||||
) {
|
) {
|
||||||
return {
|
return unsafeResult();
|
||||||
rows,
|
|
||||||
rowErrors: new Map(rows.map((item) => [item.clientKey, generalError])),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchedRow = rowByKey ?? rowByIndex!;
|
const matchedRow = rowByKey ?? rowByIndex!;
|
||||||
|
if (failedClientKeys.has(matchedRow.clientKey)) {
|
||||||
|
return unsafeResult();
|
||||||
|
}
|
||||||
failedClientKeys.add(matchedRow.clientKey);
|
failedClientKeys.add(matchedRow.clientKey);
|
||||||
rowErrors.set(matchedRow.clientKey, failure.message);
|
rowErrors.set(matchedRow.clientKey, failure.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (failedClientKeys.size !== result.failureCount) {
|
||||||
|
return unsafeResult();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rows: rows.filter((row) => failedClientKeys.has(row.clientKey)),
|
rows: rows.filter((row) => failedClientKeys.has(row.clientKey)),
|
||||||
rowErrors,
|
rowErrors,
|
||||||
|
|||||||
Reference in New Issue
Block a user