You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
3.9 KiB

4 weeks ago
# 04 · 值对象与上下文按模块化:DataVisibility / DataScopeLevel / DataVisibilityContext
Type: grilling
Status: resolved
Blocked by: 01
## Question
`DataVisibility`(现承载单一 level + 部门集合)、`DataScopeLevel`、`DataVisibilityContext`
(ThreadLocal 放一份 `DataVisibility`)如何改造成「按模块」的结构?
要定的点:
- 上下文形态:ThreadLocal 从「一份 DataVisibility」变成「module → DataVisibility 的 map」?
还是「一份持有各模块档位的聚合对象」?
- 部门集合、expandedDeptIds(子树展开)是否可跨模块共享(同一用户部门集合不随模块变,仅档位变)——
这决定改造能否只在「档位」维度分模块、而部门集合仍算一次。
- `DataVisibility` 的不可变/整体装入契约(ADR-0006「不存在设了一半」)如何在多模块下保持。
- `DataScopeLevel` 枚举本身是否需要动(大概率不动,仅其"每模块一份")。
- `currentScope()` / `currentOwnership()` 的签名如何变(读侧需按模块问 scope)。
依赖 01(模块标识)。与 05(拦截器)互相牵连,但结构决策在此先定。
## Answer
### 方案 A:一个 DataVisibility 持有 `Map<String, DataScopeLevel>`
#### 1. DataVisibility 结构变化
把单个 `level` 字段替换为 `Map<String, DataScopeLevel> moduleLevels`(key = module code,value = 档位)。
身份数据不变,只存一份:
```java
public final class DataVisibility {
private final Long userId; // 不变
private final Long primaryDeptId; // 不变
private final List<Long> deptIds; // 不变
private final List<Long> expandedDeptIds; // 不变
private final Map<String, DataScopeLevel> moduleLevels; // 新:替代单个 level
}
```
- `Map.copyOf(moduleLevels)` 封装,不可变。
- 空构造校验:`userId` 必填;`moduleLevels` 必填但**允许空 Map**(空 = 所有模块走默认 SELF)。
- `expandedDeptIds` 校验放宽:只要任一模块的 level 是 DEPT_AND_CHILDREN 就要求非 null(空 List 可以,表示一律不可见)。
#### 2. 方法签名变化
```java
// 读侧:按模块取档位 → 折算 VisibilityScope
public VisibilityScope visibilityScope(String moduleCode) {
DataScopeLevel level = moduleLevels.getOrDefault(moduleCode, DataScopeLevel.SELF);
return switch (level) {
case SELF -> VisibilityScope.ownedBy(userId);
case DEPT -> departmentsOrNone(deptIds);
case DEPT_AND_CHILDREN -> departmentsOrNone(expandedDeptIds);
case ALL -> VisibilityScope.allVisible();
};
}
// 写侧:不变
public DataOwnership dataOwnership() { ... } // 完全不动
```
#### 3. DataVisibilityContext 变化
ThreadLocal 仍持有一份 DataVisibility,但 `currentScope()` 加参数:
```java
public static VisibilityScope currentScope(String moduleCode) {
DataVisibility visibility = CURRENT.get();
return visibility != null
? visibility.visibilityScope(moduleCode)
: VisibilityScope.allVisible();
}
// currentOwnership() 完全不变
public static Optional<DataOwnership> currentOwnership() { ... }
```
#### 4. 部门集合只算一次
`deptIds` / `expandedDeptIds` 是用户级的(主部门 + 兼职并集 + 子树展开),与模块无关。
PermissionResolver 算一次,存入 DataVisibility。各模块的档位决定**用不用**这组部门 ID,不需要**重算**。
#### 5. DataScopeLevel 枚举不动
四个值(SELF/DEPT/DEPT_AND_CHILDREN/ALL)不变,每模块各选一个。枚举本身无需改。
#### 6. ADR-0006(不存在设了一半)保持
DataVisibility 仍不可变、构造成功即完整。Map 用 `Map.copyOf` 封装。空 Map 是合法状态(所有模块走 SELF)。不存在「设了一半」——要么整个上下文没有(无登录),要么整个上下文有(带完整的 moduleLevels map)。