fix(filelib): 恢复保留原名,撞名报清晰错误而非自动加后缀(ADR-0035,supersede ADR-0033)

恢复不再改名;同名兄弟占位时抛 409 name_conflict_on_restore + 人话提示,
操作者自行重命名现有节点或彻底删除旧节点后再恢复。
This commit is contained in:
ymy
2026-07-31 14:28:34 +08:00
parent a463ab48e6
commit d9cde19bdf
4 changed files with 45 additions and 44 deletions
+9 -22
View File
@@ -11,7 +11,7 @@
*/
import type { PrismaClient } from "@prisma/client";
import { FileLibError, nameKey, NODE_NAME_MAX_LENGTH } from "./model.js";
import { FileLibError, nameKey } from "./model.js";
import { FILE_LIB_AUDIT_ACTIONS, writeFileLibAudit } from "./audit.js";
import type { GroupResolver } from "./groupResolver.js";
import type { FileLibActor } from "./treeService.js";
@@ -113,7 +113,6 @@ async function requireBinEntry(
export interface RestoreResult {
readonly name: string;
readonly renamedFrom?: string | undefined;
}
/**
@@ -126,33 +125,21 @@ export async function restoreBinEntry(deps: BinDeps, actor: FileLibActor, nodeId
return deps.prisma.$transaction(async (tx) => {
const node = await requireBinEntry(tx as PrismaClient, deps, actor, groupIds, nodeId);
const siblings = await tx.fileLibNode.findMany({
const clash = await tx.fileLibNode.findFirst({
where: {
organizationId: deps.organizationId,
parentId: node.parentId,
deletedAt: null,
id: { not: node.id },
nameLower: nameKey(node.name),
},
select: { nameLower: true },
select: { id: true },
});
const taken = new Set(siblings.map((s) => s.nameLower));
let name = node.name;
let renamedFrom: string | undefined;
if (taken.has(nameKey(name))) {
renamedFrom = node.name;
let n = 1;
do {
const suffix = n === 1 ? "(已恢复)" : `(已恢复 ${n})`;
name = node.name.slice(0, NODE_NAME_MAX_LENGTH - suffix.length) + suffix;
n += 1;
} while (taken.has(nameKey(name)));
if (clash !== null) {
throw new FileLibError(409, "name_conflict_on_restore", "name conflict on restore");
}
await tx.fileLibNode.update({
where: { id: node.id },
data: { deletedAt: null, name, nameLower: nameKey(name) },
});
await tx.fileLibNode.update({ where: { id: node.id }, data: { deletedAt: null } });
await writeFileLibAudit(tx, {
action: node.kind === "PROJECT"
? FILE_LIB_AUDIT_ACTIONS.projectRestore
@@ -162,9 +149,9 @@ export async function restoreBinEntry(deps: BinDeps, actor: FileLibActor, nodeId
objectType: node.kind === "PROJECT" ? "project" : "folder",
objectId: node.id,
objectPath: node.pathIds,
detail: { name, ...(renamedFrom !== undefined ? { renamedFrom } : {}) },
detail: { name: node.name },
});
return { name, renamedFrom };
return { name: node.name };
});
}