Bläddra i källkod

1. 新增流程新建的接口

YunaiV 3 år sedan
förälder
incheckning
2607f13abe

+ 13 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmProcessInstanceController.http

@@ -0,0 +1,13 @@
+### 请求 /login 接口 => 成功
+POST {{baseUrl}}/bpm/process-instance/create
+Content-Type: application/json
+tenant-id: 1
+Authorization: Bearer {{token}}
+
+{
+  "processDefinitionId": "leave:7:20ada39c-6c95-11ec-88b1-cacd34981f8e",
+  "variables": {
+    "a": 1,
+    "b": "2"
+  }
+}

+ 35 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/BpmProcessInstanceController.java

@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Api(tags = "流程实例") // 流程实例,通过流程定义创建的一次“申请”
+@RestController
+@RequestMapping("/bpm/process-instance")
+@Validated
+public class BpmProcessInstanceController {
+
+    @Resource
+    private BpmProcessInstanceService processInstanceService;
+
+    @PostMapping("/create")
+    @ApiOperation("新建流程实例")
+    public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
+        return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
+    }
+
+}

+ 23 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/instance/BpmProcessInstanceCreateReqVO.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotEmpty;
+import java.util.Map;
+
+@ApiModel("流程实例的创建 Request VO")
+@Data
+//@EqualsAndHashCode(callSuper = true)
+//@ToString(callSuper = true)
+public class BpmProcessInstanceCreateReqVO {
+
+    @ApiModelProperty(value = "流程定义的编号", required = true, example = "1024")
+    @NotEmpty(message = "流程定义编号不能为空")
+    private String processDefinitionId;
+
+    @ApiModelProperty(value = "变量实例")
+    private Map<String, Object> variables;
+
+}

+ 6 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/task/vo/instance/BpmProcessInstanceMyPageReqVO.java

@@ -0,0 +1,6 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+
+public class BpmProcessInstanceMyPageReqVO extends PageParam {
+}

+ 7 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java

@@ -31,9 +31,13 @@ public interface BpmErrorCodeConstants {
     ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1009003000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1009003001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图");
     ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1009003002, "流程定义不存在");
+    ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003002, "流程定义处于挂起状态");
 
-    // ========== 动态表单模块 1-009-004-000 ==========
-    ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009004000, "动态表单不存在");
-    ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1009004000, "表单项({}) 和 ({}) 使用了相同的字段名({})");
+    // ========== 流程实例 1-009-004-000 ==========
+
+
+    // ========== 动态表单模块 1-009-010-000 ==========
+    ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在");
+    ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1009010000, "表单项({}) 和 ({}) 使用了相同的字段名({})");
 
 }

+ 23 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/BpmProcessInstanceService.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
+
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
+
+import javax.validation.Valid;
+
+/**
+ * 流程实例 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface BpmProcessInstanceService {
+
+    /**
+     * 创建流程实例
+     *
+     * @param userId 用户编号
+     * @param createReqVO 创建信息
+     * @return 实例的编号
+     */
+    String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO);
+
+}

+ 100 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java

@@ -0,0 +1,100 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.service.task.impl;
+
+import cn.hutool.core.lang.Assert;
+import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import lombok.extern.slf4j.Slf4j;
+import org.activiti.engine.HistoryService;
+import org.activiti.engine.RepositoryService;
+import org.activiti.engine.RuntimeService;
+import org.activiti.engine.TaskService;
+import org.activiti.engine.history.HistoricProcessInstanceQuery;
+import org.activiti.engine.impl.identity.Authentication;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.engine.runtime.ProcessInstance;
+import org.activiti.engine.task.Task;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Map;
+
+import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_IS_SUSPENDED;
+import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+/**
+ * 流程实例 Service 实现类
+ *
+ * ProcessDefinition & ProcessInstance & Execution & Task 的关系:
+ *     1. https://blog.csdn.net/bobozai86/article/details/105210414
+ *
+ * HistoricProcessInstance & ProcessInstance 的关系:
+ *     1.https://my.oschina.net/843294669/blog/719024
+ * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
+
+    @Resource
+    private RepositoryService repositoryService;
+    @Resource
+    private RuntimeService runtimeService;
+    @Resource
+    private TaskService taskService;
+    @Resource
+    private HistoryService historyService;
+
+    @Resource
+    private SysUserService userService;
+    @Resource
+    private BpmProcessDefinitionService processDefinitionService;
+
+    @Override
+    public String createProcessInstance(Long userId, BpmProcessInstanceCreateReqVO createReqVO) {
+        // 校验流程定义
+        ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
+        if (definition == null) {
+            throw exception(PROCESS_DEFINITION_NOT_EXISTS);
+        }
+        if (definition.isSuspended()) {
+            throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
+        }
+
+        // 设置流程发起人
+        Authentication.setAuthenticatedUserId(String.valueOf(userId));
+
+        // 创建流程实例
+        Map<String, Object> variables = createReqVO.getVariables();
+        variables.put("INITIATOR", userId); // TODO 芋艿:初始化人员
+        ProcessInstance instance = runtimeService.startProcessInstanceById(createReqVO.getProcessDefinitionId(), variables);
+
+        // 添加初始的评论 TODO 芋艿:在思考下
+        Task task = taskService.createTaskQuery().processInstanceId(instance.getId()).singleResult();
+        if (task != null) {
+            SysUserDO user = userService.getUser(userId);
+            Assert.notNull(user, "用户({})不存在", userId);
+            String type = "normal";
+            taskService.addComment(task.getId(), instance.getProcessInstanceId(), type,
+                    String.format("%s 发起流程申请", user.getNickname()));
+            // TODO 芋艿:应该不用下面两个步骤
+//           taskService.setAssignee(task.getId(), String.valueOf(userId));
+//            taskService.complete(task.getId(), variables);
+        }
+        return instance.getId();
+    }
+
+    public void getMyProcessInstancePage(Long userId) {
+        HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
+                .startedBy(String.valueOf(userId)) // 发起人是自己
+                .orderByProcessInstanceStartTime().desc(); // 按照发起时间倒序
+    }
+
+}

+ 8 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java

@@ -107,6 +107,14 @@ public interface SysUserService {
      */
     PageResult<SysUserDO> getUserPage(SysUserPageReqVO reqVO);
 
+    /**
+     * 获得用户
+     *
+     * @param id 用户编号
+     * @return 用户
+     */
+    SysUserDO getUser(Long id);
+
     /**
      * 获得用户列表
      *

+ 5 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java

@@ -164,6 +164,11 @@ public class SysUserServiceImpl implements SysUserService {
         return userMapper.selectPage(reqVO, this.getDeptCondition(reqVO.getDeptId()));
     }
 
+    @Override
+    public SysUserDO getUser(Long id) {
+        return userMapper.selectById(id);
+    }
+
     @Override
     public List<SysUserDO> getUsers(SysUserExportReqVO reqVO) {
         return userMapper.selectList(reqVO, this.getDeptCondition(reqVO.getDeptId()));

+ 9 - 11
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.framework.security.core.util;
 
-import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 import org.springframework.lang.Nullable;
@@ -12,7 +11,6 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
 import org.springframework.util.StringUtils;
 
 import javax.servlet.http.HttpServletRequest;
-import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -100,15 +98,15 @@ public class SecurityFrameworkUtils {
         // 原因是,Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息
         WebFrameworkUtils.setLoginUserId(request, loginUser.getId());
         WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType());
-        // TODO @jason:使用 userId 会不会更合适哈?
-        // TODO @芋艿:activiti 需要使用 ttl 上下文
-        // TODO @jason:清理问题
-        if (Objects.equals(UserTypeEnum.ADMIN.getValue(), loginUser.getUserType())) {
-            org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
-        }
-        // TODO @芋道源码 该值被赋值给 user task 中assignee , username 显示更直白一点
-        // TODO @jason:有办法设置 userId,然后 activiti 有地方读取到 username 么?毕竟 username 只是 User 的登陆账号,并不能绝对像 userId 代表一个用户
-        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
+//        // TODO @jason:使用 userId 会不会更合适哈?
+//        // TODO @芋艿:activiti 需要使用 ttl 上下文
+//        // TODO @jason:清理问题
+//        if (Objects.equals(UserTypeEnum.ADMIN.getValue(), loginUser.getUserType())) {
+//            org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
+//        }
+//        // TODO @芋道源码 该值被赋值给 user task 中assignee , username 显示更直白一点
+//        // TODO @jason:有办法设置 userId,然后 activiti 有地方读取到 username 么?毕竟 username 只是 User 的登陆账号,并不能绝对像 userId 代表一个用户
+//        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
     }
 
 }