diff --git a/docs/superpowers/specs/2026-07-09-org-tree-deepest-levels-design.md b/docs/superpowers/specs/2026-07-09-org-tree-deepest-levels-design.md new file mode 100644 index 0000000..f3ec90f --- /dev/null +++ b/docs/superpowers/specs/2026-07-09-org-tree-deepest-levels-design.md @@ -0,0 +1,62 @@ +# 组织同步 Drawer 只展示最深两层 + +**日期**:2026-07-09 +**范围**:后端 `DingTalkService.fetchOrgTreeWithUsers` + 前端 `IntegrationConfig` Drawer + +## 需求 + +钉钉组织架构树可能很深(5+ 层),「同步用户」Drawer 中只展示整棵树的最深两层,减少噪音和 API 调用。 + +- 深度以 rootDeptId=1 的全局树为准 +- 最深两层 = depth ∈ [maxDepth-1, maxDepth] +- `fetchOrgTree`(dept picker)不变 +- `syncAll` 同步逻辑不变 + +## 方案 + +### 后端:`DingTalkService` + +#### 新增 `getDeptDepthMap(token)` + +```typescript +// BFS 从 rootDeptId=1 出发,返回 Map +private async getDeptDepthMap(token: string): Promise> +``` + +不调 `getDeptDetail`,仅用 `listsubid` API 遍历树。 + +#### 修改 `fetchOrgTreeWithUsers(rootDeptId)` + +``` +depthMap = getDeptDepthMap(token) // 全局深度 +maxDepth = max(depthMap.values()) +subtree = getAllDeptIds(token, rootDeptId) // 子树范围 +filtered = subtree.filter(d => depthMap.get(d) ∈ [maxDepth-1, maxDepth]) + +for each filtered: getDeptDetail + getDeptUsers +assemble tree(父节点不在 filtered 中的提升为根) +``` + +#### 边界 + +|情况|行为| +|---|---| +|树只有 1 层|取 depth=1,展示根部门| +|树 2 层|取 depth=1,2,展示全部(和现在一致)| +|子树最深层 < maxDepth-1|返回空数组| +|中间层有用户|不展示(被裁剪)| + +### 前端 + +零改动。返回数据仍是 `DingOrgTreeNodeWithUsers[]`,只是节点变少。空树时现有 `` 兜底。 + +## 不受影响 + +- `fetchOrgTree` — dept picker 仍显示完整树 +- `syncAll` — 同步全部部门 +- `getAllDeptIds` / `getDeptDetail` / `getDeptUsers` — 不变 + +## 改动量 + +- `dingtalk.service.ts`:约 30 行(新方法 + 改旧方法) +- 前端:0 行