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
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)。
|