feat(dept): 科室管理——flw_dept/flw_dept_member 组+成员模型取代单用户映射行(议题3,拍板D1选B)
build / build-push (push) Successful in 54s

- deptKey=本地自增 id;source_group_id 幂等(一键拉取=初始化/刷新语义,不重复建组);不做存量迁移
- 四处读源全部切新表:AssigneeResolver(宽容口径:deptKey 不存在/停用返回空,图示/轨迹不炸)、
  enabledDeptKeys 发布校验白名单、DeptLabelSupport 唯一出口(图示科室视图+轨迹富化)、编辑器 deptList 聚合
- /admin/dept/* 全套端点(CRUD/成员/拉取预览导入);旧 /map/* 映射 CRUD 端点删除
  (保留 /map/lookupUser 选人检索与 /user/teacherTree);flw_assignee_map 实体/表保留不删
- IT 种子切源(FlowTestSupport tag→deptKey 注册表,用例主体零改动);全量 191 绿;E2E 22/22
  (local-debug/four-topics-e-e2e.py,含切源后果验证与生产盘点 SQL)
This commit is contained in:
chenxing
2026-10-10 19:47:58 +08:00
parent 4293c9628a
commit a06b610d9d
18 changed files with 865 additions and 159 deletions
@@ -1,7 +1,7 @@
package com.yunhai.flowcenter.engine;
import com.yunhai.flowcenter.entity.FlwAssigneeMap;
import com.yunhai.flowcenter.repository.FlwAssigneeMapRepository;
import com.yunhai.flowcenter.entity.FlwDeptMember;
import com.yunhai.flowcenter.service.FlowDeptAdminService;
import com.yunhai.platform.apistd.BizException;
import com.yunhai.platform.openapi.base.OpenApiUserClient;
import com.yunhai.platform.utils.exception.ErrorCode;
@@ -14,15 +14,15 @@ import java.util.ArrayList;
import java.util.List;
/**
* 办理人解析(概览 §六):DEPT_KEY 查显式映射表(零跨服务依赖);USERS 定义直配,
* 姓名经 UserBase /oapi/user/byId 补全(失败降级空名不阻塞)。SUBMITTER_DEPT/FORM_FIELD 二期。
* 办理人解析(概览 §六):DEPT_KEY 查科室管理(flw_dept+flw_dept_member,2026-10-10 切源,
* 零跨服务依赖);USERS 定义直配,姓名经 UserBase /oapi/user/byId 补全(失败降级空名不阻塞)。
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AssigneeResolver {
private final FlwAssigneeMapRepository mapRepository;
private final FlowDeptAdminService deptAdminService;
private final OpenApiUserClient userClient;
public List<AssigneeRef> resolve(Long schId, FlowDefModel.Node node) {
@@ -32,8 +32,7 @@ public class AssigneeResolver {
}
List<AssigneeRef> refs = new ArrayList<>();
if (FlowDefModel.ASSIGNEE_DEPT_KEY.equals(a.type)) {
List<FlwAssigneeMap> maps = mapRepository.findEnabled(schId, a.deptKey);
for (FlwAssigneeMap m : maps) {
for (FlwDeptMember m : deptAdminService.resolveEnabled(schId, a.deptKey)) {
refs.add(new AssigneeRef(m.getUserId(), m.getUserName()));
}
} else if (FlowDefModel.ASSIGNEE_USERS.equals(a.type)) {
@@ -47,14 +46,14 @@ public class AssigneeResolver {
}
if (refs.isEmpty()) {
throw new BizException(ErrorCode.PARAM_ERROR,
"节点[" + node.id + "]无可用办理人(映射可能已被停用)");
"节点[" + node.id + "]无可用办理人(科室可能已被停用或成员为空)");
}
return refs;
}
/**
* 抄送接收人解析(NOTIFY 专用,宽容口径):结构不合法照抛(发布期校验本应拦住,属脏定义),
* 但解析不出人(映射被停用/名单为空)只返回空列表由调用方记日志——抄送是知会不是审批,
* 但解析不出人(科室被停用/名单为空)只返回空列表由调用方记日志——抄送是知会不是审批,
* 绝不能因抄送对象缺失卡住流程推进。与 {@link #resolve} 的强口径相反。
*/
public List<AssigneeRef> resolveTolerant(Long schId, FlowDefModel.Node node) {
@@ -64,7 +63,7 @@ public class AssigneeResolver {
}
List<AssigneeRef> refs = new ArrayList<>();
if (FlowDefModel.ASSIGNEE_DEPT_KEY.equals(a.type)) {
for (FlwAssigneeMap m : mapRepository.findEnabled(schId, a.deptKey)) {
for (FlwDeptMember m : deptAdminService.resolveEnabled(schId, a.deptKey)) {
refs.add(new AssigneeRef(m.getUserId(), m.getUserName()));
}
} else if (FlowDefModel.ASSIGNEE_USERS.equals(a.type)) {