feat(filelib): 移除最近打开模块(ADR-0032,supersede ADR-0031 对应半部)

- FileLibRecentVisit 删表(手写迁移;表当日新建无生产数据)
- recentService/recentRoutes/RecentView 删除;GridLibraryView 埋点与
  navTarget 跳转一并移除;types 清 RecentEntry
- 左栏保留 文件库/回收站(ADR-0031 回收站半部不受影响)
- breadcrumb 的 role 字段保留(独立可用的增量字段)
This commit is contained in:
ymy
2026-07-31 13:39:18 +08:00
parent 475b397ca8
commit 83a6b012b7
11 changed files with 38 additions and 366 deletions
+2 -50
View File
@@ -1,8 +1,7 @@
/**
* 回收站 + 最近打开集成测试(真实 Postgres,ADR-0031)。
* 回收站集成测试(真实 Postgres,ADR-0031;最近打开已由 ADR-0032 移除)。
* 覆盖:bin 列出(祖先全活跃顶点/直连 MANAGE 可见性/管理员)、restore 对称语义
* 与审计、purge 仅管理员 + 整支硬删(RESTRICT 顺序)、recent 上报 VIEW 门禁 /
* upsert 刷新 / 删除与祖先删除的可见性过滤。
* 与审计、purge 仅管理员 + 整支硬删(RESTRICT 顺序)
* 运行前提:本地 PG(paradigm:paradigm@127.0.0.1:5432/cph_hub_test)且已 migrate。
*/
import { beforeEach, describe, expect, it } from "vitest";
@@ -11,12 +10,10 @@ import {
createNode,
softDeleteNode,
listChildren,
getEffectiveRole,
type FileLibActor,
type TreeServiceDeps,
} from "../../src/database/filelib/treeService.js";
import { listBin, purgeBinEntry, restoreBinEntry, type BinDeps } from "../../src/database/filelib/binService.js";
import { listRecent, recordVisit, type RecentDeps } from "../../src/database/filelib/recentService.js";
import { createStaticGroupResolver } from "../../src/database/filelib/groupResolver.js";
import { createInMemoryVersionStore } from "../../src/database/filelib/versionStore.js";
import { FILE_LIB_AUDIT_ACTIONS } from "../../src/database/filelib/audit.js";
@@ -43,14 +40,6 @@ function binDeps(): BinDeps {
};
}
function recentDeps(): RecentDeps {
return {
prisma,
organizationId: DEFAULT_ORG_ID,
groupResolver: createStaticGroupResolver({ u_bob: ["g_physics"] }),
};
}
beforeEach(async () => {
await resetDb();
for (const [id, openId] of [["u_admin", "ou_admin"], ["u_alice", "ou_alice"], ["u_bob", "ou_bob"]] as const) {
@@ -124,40 +113,3 @@ describe("binService · 彻底删除", () => {
expect(audits).toHaveLength(1);
});
});
describe("recentService · 最近打开", () => {
it("上报需 VIEW(404);upsert 刷新 openedAt;删除/祖先删除的条目被过滤", async () => {
const root = await createNode(treeDeps(), ADMIN, { parentId: null, kind: "FOLDER", name: "物理" });
const proj = await createNode(treeDeps(), ADMIN, { parentId: root.id, kind: "PROJECT", name: "TH-141" });
const secret = await createNode(treeDeps(), ADMIN, { parentId: null, kind: "FOLDER", name: "秘密" });
// bob 对 secret 无 VIEW → 404
await expect(recordVisit(recentDeps(), BOB, secret.id, undefined)).rejects.toMatchObject({ statusCode: 404 });
// admin 上报:root、proj、proj 内文件
await recordVisit(recentDeps(), ADMIN, root.id, undefined);
await recordVisit(recentDeps(), ADMIN, proj.id, undefined);
await recordVisit(recentDeps(), ADMIN, proj.id, "讲义/第一章.md");
let entries = await listRecent(recentDeps(), ADMIN);
expect(entries).toHaveLength(3);
expect(entries.map((e) => e.filePath)).toContain("讲义/第一章.md");
// upsert:重复打开 root 只刷新,不新增
await recordVisit(recentDeps(), ADMIN, root.id, undefined);
entries = await listRecent(recentDeps(), ADMIN);
expect(entries).toHaveLength(3);
expect(entries[0]!.nodeId).toBe(root.id); // 最新在前
// 删祖先 → 整支条目消失
await softDeleteNode(treeDeps(), ADMIN, root.id);
entries = await listRecent(recentDeps(), ADMIN);
expect(entries).toEqual([]);
// 恢复后重新可见
await restoreBinEntry(binDeps(), ADMIN, root.id);
entries = await listRecent(recentDeps(), ADMIN);
expect(entries).toHaveLength(3);
expect(await getEffectiveRole(treeDeps(), ADMIN, proj.id)).toBe("MANAGE");
});
});