Skip to content
bitzorcas
中EN

Concept

DocumentStructure 模板、收藏与回收站协调

源码校验的 DocumentStructure 总览,讲清文件夹模板、节点图、历史快照、模板应用、用户收藏、Documents 回收站端口和当前商业边界。

Last updated

DocumentStructure 不是通用“文档目录树”模块。它拥有三块能力:可复用的文件夹模板及节点快照、当前用户的资源收藏关系,以及协调 Documents 软删除对象的回收站应用端口。真正的 DocumentCategory、文档正文、知识库和回收站 Store 实现仍归 Documents。

1. 当前真实能力

已经实现:

  • 租户内唯一命名的文件夹模板;
  • 当前节点集合与父子临时 ID 到持久 ID 的完整重映射;
  • 节点 ID 非空、同请求唯一、父节点存在、无环、最大深度 10;
  • 更新时保存被替换节点集合的 AOT-safe JSON 快照;
  • 模板列表、详情、创建、更新、删除、预览和应用;
  • Scriban 动态名称表达式的编译和渲染;
  • 当前用户的收藏新增、列表和本人删除;
  • Document/Category 回收站列表、恢复和彻底删除分派;
  • Store 同时约束调用租户与 ICurrentTenant.EffectiveTenantId;
  • SqlSugar / EF Core 共用 ORM 中立持久化元数据与测试基础。

当前没有:模板历史查询、版本 diff、回滚、发布审批、并发 ETag、应用幂等、批量原子分类创建、失败补偿、目标类型白名单、知识库实例授权、Scriban 错误报告、收藏资源解析、收藏权限目录、Purge 保留/法律留存策略和完整 HTTP 安全测试。

2. 模块结构

认证客户端

13 条 HTTP 路由
11 生成 + 2 手写

IAuthorizedRequest
Resource 由请求类型约定推导

模板用例
图校验 · 快照 · 预览 · 应用

收藏用例
用户关系

回收站用例
查询 · 恢复 · Purge

DocumentStructureStore
4 张 owner-local 表

IRepository
Documents Application

DocumentRecycleBinStore
Documents Infrastructure

DocumentStructure Application 当前直接引用 Documents Application 和 Contracts;模板应用直接依赖 IRepository<DocumentCategory>。回收站方向相反:端口由 DocumentStructure Application 拥有,Documents Infrastructure 实现。前者扩大了模块耦合,后者符合 owner adapter 的组合方向。

3. 十三条 HTTP 路由

方法与路由用途实现方式
POST /api/v1/document-structures/templates创建模板生成
GET /api/v1/document-structures/templates列表与筛选生成
GET /api/v1/document-structures/templates/{id}模板详情生成
PUT /api/v1/document-structures/templates/{id}更新与生成快照生成
DELETE /api/v1/document-structures/templates/{id}软删模板、删当前节点生成
POST /api/v1/document-structures/templates/{id}/preview动态名称预览手写
POST /api/v1/document-structures/templates/{id}/apply创建分类树手写
POST /api/v1/document-structures/favorites新增收藏生成
GET /api/v1/document-structures/favorites当前用户收藏生成
DELETE /api/v1/document-structures/favorites/{id}删除本人收藏生成
GET /api/v1/document-structures/recycle-bin回收站分页生成
POST /api/v1/document-structures/recycle-bin/{id}/restore恢复 Document/Category生成
DELETE /api/v1/document-structures/recycle-bin/{id}彻底删除生成

手写路由组应用认证、userPolicy 限流和标准超时。生成请求均实现 IAuthorizedRequest,但没有显式覆写 Resource,依赖 AuthorizationResourceConventions.FromRequestType。权限目录定义模板与回收站权限,却没有 Favorite 权限常量;必须用真实 HTTP policy 测试证明约定映射,而不能只看常量存在。

4. 一条真实创建模板路径

创建两层文件夹模板
var result = await mediator.Send(new CreateFolderTemplate.Command(
Name: "案件标准目录",
Description: "为每个案件创建材料与交付目录",
TargetType: "KnowledgeBase",
Nodes:
[
// Id 是本次请求内临时标识;Store 会分配最终主键。
new("root-material", null, "材料", null, "folder", "#2563eb", null, 10),
new("child-contract", "root-material", "合同", null, "file", "#475569", null, 20)
]), cancellationToken);
if (result.IsFailure)
return result.Error;
// 返回节点使用持久化 Id,ParentNodeId 也已同步重映射。
return result.Value!;

Handler 先检查模板名称非空与租户内重名,再验证完整图,最后交给 Store 写根和节点。数据库对 (TenantId, Name) 有唯一约束,应用层“先查后写”仍可能在并发时冲突;当前没有把唯一异常映射为稳定 Conflict。

5. 应用模板的真实时序

"IRepository<DocumentCategory>""Scriban compiler""DocumentStructureStore""ApplyTemplate.Handler""IRepository<DocumentCategory>""Scriban compiler""DocumentStructureStore""ApplyTemplate.Handler"loop["顶层后逐层子节点"]"用户"TemplateId + TargetId + NamingContexttenant-scoped template + nodescompile/render or fallback NameSaveAsync one categoryCategoryId or failureCreatedCategoryIds"用户"

TargetType 没有与模板 TargetType 比较,也没有决定不同 owner;无论传什么,代码都把 TargetId 当 KnowledgeBaseId 创建 DocumentCategory。Handler 没有显式验证知识库存在或当前用户可管理它。

6. 收藏不是授权

收藏表唯一键是 TenantId、UserId、TargetType、TargetId。新增只检查类型和 ID 非空,不检查类型白名单、目标存在或 owner 权限,并在 UserId 缺失时写 "0"。列表只按当前租户/用户/分组读关系,不回源过滤已删除或失权对象。

因此 FavoriteItem 只是引用,不是可访问资源,也不是目标详情。消费端必须通过 owner 的授权查询解析它;更好的 GA 契约是在模块服务端批量解析并删除/标记悬空项。

7. 回收站是跨模块协调面

IDocumentRecycleBinStore 定义在 DocumentStructure Application,真正实现位于 Documents Infrastructure。Handler 只接受 ItemType=Document 或 Category 并分派。Query 的 KnowledgeBaseId、PageIndex、PageSize 原样交给 Store。

当前返回成功不证明对象存在,也不返回受影响数量。分类的递归恢复/Purge、父级冲突、附件、索引、版本、收藏与事件如何处理,需要以 Documents Store 和集成测试为准。

8. 章节路线

9. GA 红线

  1. 模板应用先验证当前租户、目标类型、知识库存在和对象级管理权限;
  2. 整棵树使用原子批量创建,或具备可证明的补偿与恢复;
  3. 无法解析的节点必须失败,不能用部分 CreatedCategoryIds 冒充完整成功;
  4. 引入 ApplicationId 幂等键,重复与并发不重复创建分类;
  5. Scriban 使用真异步取消、资源限制、允许变量/函数和可见错误;
  6. 快照 schema/version 可读取、diff、回滚和迁移;
  7. 模板并发更新使用版本条件/ETag,唯一冲突类型化;
  8. 收藏要求真实 UserId、TargetType allowlist、owner 存在性与授权;
  9. 回收站读写绑定 KnowledgeBase 和具体对象权限,Purge 受保留/法律留存控制;
  10. 生成端点资源约定、Favorite 权限和权限目录以 HTTP 测试固定;
  11. 列表消除逐模板 Count 的 N+1,并建立大租户/大模板容量基线;
  12. 双 ORM 事务、并发、故障、恢复和运维证据进入发布门禁。

10. 源码导航

Terminal window
# 核对 11 条生成路由与 2 条手写路由。
rg -n "GenerateEndpoint\(|MapPost\(" \
src/Platform/DocumentStructure src/Hosts/BitzOrcas.Api/Endpoints/DocumentStructureEndpoints.cs -g '*.cs'
# 暴露模板应用的直接 Documents 依赖、同步阻塞和静默回退。
rg -n "IRepository<DocumentCategory>|GetAwaiter\(\)\.GetResult|CancellationToken.None|return node.Name" \
src/Platform/DocumentStructure -g '*.cs'
# 当前应无应用幂等、版本回滚与收藏 owner resolver。
rg -n "ApplicationId|RollbackTemplate|FavoriteTargetResolver|RowVersion" \
src/Platform/DocumentStructure -g '*.cs'

返回模块目录 · Documents 模块 · Authorization 模块

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%