4-10 树域 Rust 家族化

This commit is contained in:
lix-2026
2026-04-24 06:10:18 +08:00
parent 41e958769e
commit 94631f3636
49 changed files with 5751 additions and 557 deletions
@@ -20,7 +20,7 @@ describe("tree-command-client", () => {
it("通过统一 client 发送 tree/document command 并返回结果", async () => {
const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue({
ok: true,
json: async () => ({ ok: true, success: true, items: [] }),
json: async () => ({ ok: true, success: true, items: [], id: "doc_created", documentId: "doc_created" }),
} as Response);
await createDocumentCommand(null);
@@ -41,9 +41,9 @@ describe("tree-command-client", () => {
expect(fetchMock).toHaveBeenCalledTimes(10);
expect(fetchMock.mock.calls.map((call) => String(call[0]))).toEqual([
"/api/documents/create",
"/api/documents/title",
"/api/documents/move",
"/api/tree/commands",
"/api/tree/commands",
"/api/tree/commands",
"/api/documents/delete",
"/api/documents/restore",
"/api/documents/purge",
@@ -52,6 +52,24 @@ describe("tree-command-client", () => {
"/api/documents/title",
"/api/documents/options",
]);
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
action: "create",
parentId: null,
});
expect(JSON.parse(String(fetchMock.mock.calls[1]?.[1]?.body))).toEqual({
action: "rename",
documentId: "doc_1",
workspaceId: null,
title: "新标题",
});
expect(JSON.parse(String(fetchMock.mock.calls[2]?.[1]?.body))).toEqual({
action: "move",
documentId: "doc_1",
parentId: null,
sortOrder: 0,
workspaceId: null,
});
});
it("在后端返回错误时抛出统一异常", async () => {
@@ -96,6 +96,8 @@ type MoveDocumentInput = {
workspaceId?: string | null;
};
type TreeCommandAction = "create" | "rename" | "move";
type DeleteDocumentInput = {
documentId: string;
workspaceId?: string | null;
@@ -148,8 +150,56 @@ async function postDocumentCommand<TResult>(path: string, payload: unknown, fall
return body as TResult;
}
type TreeCommandResponse = {
requestId?: string;
traceId?: string;
result?: {
action?: TreeCommandAction;
workspaceId?: string | null;
documentId?: string;
parentId?: string | null;
title?: string | null;
sortOrder?: number | null;
updatedAt?: string | null;
execution?: {
access_scope?: "private" | "shared" | "public";
is_template?: boolean;
created_at?: string | null;
updated_at?: string | null;
} | null;
} | null;
};
async function postTreeCommand<TResult>(payload: unknown, fallbackMessage: string): Promise<TResult> {
return postDocumentCommand<TResult>("/api/tree/commands", payload, fallbackMessage);
}
export async function createDocumentCommand(parentId: string | null): Promise<DocumentCreateCommandResult> {
return postDocumentCommand<DocumentCreateCommandResult>("/api/documents/create", { parentId }, "新建页面失败,请稍后再试");
const response = await postTreeCommand<TreeCommandResponse>(
{
action: "create",
parentId,
},
"新建页面失败,请稍后再试",
);
const result = response.result;
return {
id: result?.documentId ?? "",
title: result?.title ?? "无标题",
parent_id: result?.parentId ?? parentId,
sort_order: result?.sortOrder ?? null,
workspace_id: result?.workspaceId ?? undefined,
access_scope: result?.execution?.access_scope ?? "private",
is_template: result?.execution?.is_template ?? false,
created_at: result?.execution?.created_at ?? null,
updated_at: result?.execution?.updated_at ?? result?.updatedAt ?? null,
meta: {
requestId: response.requestId,
traceId: response.traceId,
commandName: TREE_COMMAND_PROTOCOL.create.preferredCommandName,
},
};
}
export async function createChildDocumentCommand(
@@ -163,15 +213,24 @@ export async function createChildDocumentCommand(
}
export async function renameDocumentCommand(input: RenameDocumentInput): Promise<{ ok: true; meta?: DocumentCommandMeta }> {
return postDocumentCommand<{ ok: true; meta?: DocumentCommandMeta }>(
"/api/documents/title",
const response = await postTreeCommand<TreeCommandResponse>(
{
action: "rename",
documentId: input.documentId,
workspaceId: input.workspaceId ?? null,
title: input.title,
},
"重命名失败,请稍后再试",
);
return {
ok: true,
meta: {
requestId: response.requestId,
traceId: response.traceId,
commandName: TREE_COMMAND_PROTOCOL.rename.preferredCommandName,
},
};
}
export async function updatePageTitleCommand(
@@ -192,16 +251,25 @@ export async function updatePageOptionsCommand(
}
export async function moveDocumentCommand(input: MoveDocumentInput): Promise<{ ok: true; meta?: DocumentCommandMeta }> {
return postDocumentCommand<{ ok: true; meta?: DocumentCommandMeta }>(
"/api/documents/move",
const response = await postTreeCommand<TreeCommandResponse>(
{
action: "move",
documentId: input.documentId,
parentId: input.parentId ?? null,
position: input.position,
sortOrder: Number.isFinite(input.position) ? Math.floor(input.position) : 0,
workspaceId: input.workspaceId ?? null,
},
"移动失败,请稍后再试",
);
return {
ok: true,
meta: {
requestId: response.requestId,
traceId: response.traceId,
commandName: TREE_COMMAND_PROTOCOL.move.preferredCommandName,
},
};
}
export async function deleteDocumentCommand(