Browse Source

feat: 转办/加签可选用户按节点审批角色过滤

- 新增 /flow/task/{taskId}/transferable-users 接口
- 根据流程定义模型解析当前节点 assigneeType/assigneeValue
- ROLE 节点返回该角色下已分配的系统用户
- 非 ROLE 节点回退为全部可用系统用户
- 自动排除当前登录人
ye-zhaojia 1 tuần trước cách đây
mục cha
commit
0f4fa28e8f

+ 7 - 0
src/main/java/com/qqflow/engine/domain/flow/controller/ApprovalTaskController.java

@@ -12,6 +12,7 @@ import com.qqflow.engine.domain.flow.dto.BatchTaskDTO;
 import com.qqflow.engine.domain.flow.dto.NextNodeDTO;
 import com.qqflow.engine.domain.flow.dto.TransferTaskDTO;
 import com.qqflow.engine.domain.flow.service.ApprovalTaskService;
+import com.qqflow.engine.domain.system.dto.UserDTO;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
@@ -147,6 +148,12 @@ public class ApprovalTaskController {
         return Result.ok();
     }
 
+    @GetMapping("/{taskId}/transferable-users")
+    @Operation(summary = "获取可转办/加签的用户列表")
+    public Result<List<UserDTO>> transferableUsers(@PathVariable Long taskId) {
+        return Result.ok(this.approvalTaskService.getTransferableUsers(taskId));
+    }
+
     private LoginUser requireLoginUser() {
         LoginUser loginUser = SecurityUtils.getLoginUser();
         if (loginUser == null) {

+ 4 - 0
src/main/java/com/qqflow/engine/domain/flow/service/ApprovalTaskService.java

@@ -7,6 +7,7 @@ import com.qqflow.engine.domain.flow.dto.ApproveTaskDTO;
 import com.qqflow.engine.domain.flow.dto.BatchTaskDTO;
 import com.qqflow.engine.domain.flow.dto.NextNodeDTO;
 import com.qqflow.engine.domain.flow.dto.TransferTaskDTO;
+import com.qqflow.engine.domain.system.dto.UserDTO;
 
 import java.util.List;
 
@@ -40,4 +41,7 @@ public interface ApprovalTaskService {
 
     /** 获取下游节点列表(条件节点分支选择用) */
     List<NextNodeDTO> getNextNodes(Long taskId);
+
+    /** 获取可转办/加签的用户列表(按当前节点审批角色过滤) */
+    List<UserDTO> getTransferableUsers(Long taskId);
 }

+ 97 - 0
src/main/java/com/qqflow/engine/domain/flow/service/impl/ApprovalTaskServiceImpl.java

@@ -27,6 +27,7 @@ import com.qqflow.engine.domain.flow.mapper.AttachmentMapper;
 import com.qqflow.engine.domain.flow.mapper.ProcessDefinitionMapper;
 import com.qqflow.engine.domain.flow.mapper.ProcessInstanceMapper;
 import com.qqflow.engine.domain.flow.model.FlowModel;
+import com.qqflow.engine.domain.flow.model.FlowNode;
 import com.qqflow.engine.domain.flow.po.ApprovalRecord;
 import com.qqflow.engine.domain.flow.po.ApprovalTask;
 import com.qqflow.engine.domain.flow.po.Attachment;
@@ -34,8 +35,11 @@ import com.qqflow.engine.domain.flow.po.ProcessDefinition;
 import com.qqflow.engine.domain.flow.po.ProcessInstance;
 import com.qqflow.engine.domain.flow.service.ApprovalTaskService;
 import com.qqflow.engine.domain.flow.service.FlowEngineService;
+import com.qqflow.engine.domain.system.dto.UserDTO;
+import com.qqflow.engine.domain.system.entity.SysRole;
 import com.qqflow.engine.domain.system.entity.SysUser;
 import com.qqflow.engine.domain.system.entity.SysUserRole;
+import com.qqflow.engine.domain.system.mapper.SysRoleMapper;
 import com.qqflow.engine.domain.system.mapper.SysUserMapper;
 import com.qqflow.engine.domain.system.mapper.SysUserRoleMapper;
 import com.qqflow.engine.domain.system.service.SysUserService;
@@ -52,6 +56,7 @@ import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
@@ -80,6 +85,7 @@ public class ApprovalTaskServiceImpl implements ApprovalTaskService {
     private final ApplicationEventPublisher eventPublisher;
     private final SysUserRoleMapper sysUserRoleMapper;
     private final SysUserMapper sysUserMapper;
+    private final SysRoleMapper sysRoleMapper;
     private final SysUserService sysUserService;
 
     @Override
@@ -578,4 +584,95 @@ public class ApprovalTaskServiceImpl implements ApprovalTaskService {
                     return dto;
                 }).collect(Collectors.toList());
     }
+
+    @Override
+    public List<UserDTO> getTransferableUsers(Long taskId) {
+        ApprovalTask task = this.getPendingTask(taskId);
+        this.checkTaskPermission(task);
+        Long operatorId = SecurityUtils.getUserId();
+
+        ProcessInstance instance = this.processInstanceMapper.selectById(task.getInstanceId());
+        if (instance == null) {
+            throw new BusinessException("流程实例不存在");
+        }
+        ProcessDefinition definition = this.processDefinitionMapper.selectById(instance.getProcessDefinitionId());
+        if (definition == null || definition.getModelJson() == null) {
+            return this.listAllActiveSystemUsers(operatorId);
+        }
+
+        FlowModel model = this.flowEngineService.parseModel(definition.getModelJson());
+        if (model == null || CollectionUtils.isEmpty(model.getNodes())) {
+            return this.listAllActiveSystemUsers(operatorId);
+        }
+
+        FlowNode currentNode = model.getNodes().stream()
+                .filter(n -> n.getId() != null && n.getId().equals(task.getNodeId()))
+                .findFirst()
+                .orElse(null);
+        if (currentNode == null) {
+            return this.listAllActiveSystemUsers(operatorId);
+        }
+
+        Map<String, Object> props = currentNode.getProperties();
+        String assigneeType = ASSIGNEE_TYPE_ROLE;
+        String assigneeValue = null;
+        if (props != null) {
+            Object typeObj = props.get("assigneeType");
+            Object valueObj = props.get("assigneeValue");
+            if (typeObj != null) {
+                assigneeType = typeObj.toString();
+                assigneeValue = valueObj != null ? valueObj.toString() : null;
+            } else {
+                Object approverObj = props.get("approver");
+                if (approverObj != null) {
+                    assigneeValue = approverObj.toString();
+                }
+            }
+        }
+
+        if (!ASSIGNEE_TYPE_ROLE.equals(assigneeType) || assigneeValue == null) {
+            return this.listAllActiveSystemUsers(operatorId);
+        }
+
+        SysRole role = this.sysRoleMapper.selectByRoleCode(assigneeValue);
+        if (role == null) {
+            return Collections.emptyList();
+        }
+
+        List<SysUserRole> userRoles = this.sysUserRoleMapper.selectList(
+                new LambdaQueryWrapper<SysUserRole>()
+                        .eq(SysUserRole::getRoleId, role.getId()));
+        List<Long> userIds = userRoles.stream()
+                .map(SysUserRole::getUserId)
+                .distinct()
+                .collect(Collectors.toList());
+        if (userIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<SysUser> users = this.sysUserMapper.selectList(
+                new LambdaQueryWrapper<SysUser>()
+                        .in(SysUser::getId, userIds)
+                        .eq(SysUser::getStatus, 0)
+                        .orderByAsc(SysUser::getRealName, SysUser::getId));
+
+        return users.stream()
+                .filter(u -> !u.getId().equals(operatorId))
+                .map(UserDTO::of)
+                .collect(Collectors.toList());
+    }
+
+    private List<UserDTO> listAllActiveSystemUsers(Long excludeUserId) {
+        List<SysUser> users = this.sysUserMapper.selectList(
+                new LambdaQueryWrapper<SysUser>()
+                        .eq(SysUser::getStatus, 0)
+                        .orderByAsc(SysUser::getRealName, SysUser::getId));
+        if (excludeUserId == null) {
+            return users.stream().map(UserDTO::of).collect(Collectors.toList());
+        }
+        return users.stream()
+                .filter(u -> !u.getId().equals(excludeUserId))
+                .map(UserDTO::of)
+                .collect(Collectors.toList());
+    }
 }