fix(任务): 修复快速创建导致的重复ID
This commit is contained in:
27
apps/web/lib/entity-id.test.ts
Normal file
27
apps/web/lib/entity-id.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { createEntityId, dedupeEntityIds } from './entity-id';
|
||||
|
||||
test('createEntityId stays unique within the same millisecond', () => {
|
||||
const ids = Array.from({ length: 20 }, () => createEntityId('task', 1782361373857));
|
||||
|
||||
assert.equal(new Set(ids).size, ids.length);
|
||||
});
|
||||
|
||||
test('dedupeEntityIds rewrites duplicate ids while preserving the first item id', () => {
|
||||
const result = dedupeEntityIds(
|
||||
[
|
||||
{ id: 'task-1', title: 'A' },
|
||||
{ id: 'task-1', title: 'B' },
|
||||
{ id: 'task-2', title: 'C' },
|
||||
{ id: 'task-1', title: 'D' },
|
||||
],
|
||||
'task',
|
||||
1782361373857,
|
||||
);
|
||||
|
||||
assert.equal(result.changed, true);
|
||||
assert.equal(result.items[0].id, 'task-1');
|
||||
assert.equal(new Set(result.items.map((item) => item.id)).size, result.items.length);
|
||||
});
|
||||
43
apps/web/lib/entity-id.ts
Normal file
43
apps/web/lib/entity-id.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
let lastTimestamp = 0;
|
||||
let sameTimestampSequence = 0;
|
||||
|
||||
export interface DedupeEntityIdsResult<T> {
|
||||
items: T[];
|
||||
changed: boolean;
|
||||
}
|
||||
|
||||
export function createEntityId(prefix: string, timestamp = Date.now()): string {
|
||||
if (timestamp === lastTimestamp) {
|
||||
sameTimestampSequence += 1;
|
||||
} else {
|
||||
lastTimestamp = timestamp;
|
||||
sameTimestampSequence = 0;
|
||||
}
|
||||
return `${prefix}-${timestamp}-${sameTimestampSequence.toString(36)}`;
|
||||
}
|
||||
|
||||
export function dedupeEntityIds<T extends { id: string }>(
|
||||
items: T[],
|
||||
prefix: string,
|
||||
timestamp = Date.now(),
|
||||
): DedupeEntityIdsResult<T> {
|
||||
const seen = new Set<string>();
|
||||
let changed = false;
|
||||
|
||||
const deduped = items.map((item) => {
|
||||
if (item.id && !seen.has(item.id)) {
|
||||
seen.add(item.id);
|
||||
return item;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
let nextId = createEntityId(prefix, timestamp);
|
||||
while (seen.has(nextId)) {
|
||||
nextId = createEntityId(prefix, timestamp);
|
||||
}
|
||||
seen.add(nextId);
|
||||
return { ...item, id: nextId };
|
||||
});
|
||||
|
||||
return { items: changed ? deduped : items, changed };
|
||||
}
|
||||
Reference in New Issue
Block a user