Activiti工作流会签一 部署流程

这里使用的方式是和springboot整合的activiti工作流5.22.0版。 并且是在线编辑流程图
的,关于整合请自动百度,我应该在后面也会写一篇文章。

创建模型

ActModelController.java

首先在自定义的controller类中注册下面两个Bean对象:
repositoryService的主要作用是管理流程仓库,例如部署,删除。 repositoryService详细介绍
ObjectMapper 是fastjson的Bean,主要作用是对一些内容进行json转化。

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    ObjectMapper objectMapper;
 @RequestMapping(value = "/add", method = RequestMethod.POST)
    @ApiOperation(value = "创建新模型")
    public Result<Object> addModel(ActModel actModel){

        // 初始化一个空模型
        Model model = repositoryService.newModel();

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, actModel.getName());
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, actModel.getDescription());
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, model.getVersion());

        model.setName(actModel.getName());
        model.setKey(actModel.getModelKey());
        model.setMetaInfo(modelNode.toString());

        // 保存模型
        repositoryService.saveModel(model);
        String id = model.getId();

        // 完善ModelEditorSource
        ObjectNode editorNode = objectMapper.createObjectNode();
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        ObjectNode properties = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.replace("stencilset", stencilSetNode);
        properties.put("process_id", actModel.getModelKey());
        editorNode.replace("properties", properties);
        try {
            repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            log.error(e.toString());
            return ResultUtil.error("添加模型失败");
        }

        // 保存扩展模型至数据库
        actModel.setId(id);
        actModel.setVersion(model.getVersion());
        actModelService.save(actModel);
        return ResultUtil.success("添加模型成功");
    }

把模型名称, 标型存到自行创建的表中,方便管理,查询。

@Data
@TableName("t_act_model")
@ApiModel(value = "模型")
public class ActModel extends TbootBaseEntity {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "模型名称")
    private String name;

    @ApiModelProperty(value = "标识")
    private String modelKey;

    @ApiModelProperty(value = "版本")
    private Integer version;

    @ApiModelProperty(value = "描述/备注")
    private String description;
}

会签画图

会签 : 多个人,同时批准才能进入到下一步流程。

Activiti工作流会签一 部署流程_第1张图片

这是一个简单的会签图, 下面我们会简单的说一下会签的画图流程。

画完流程图,对流程图进行保存

public class ModelSaveRestResource implements ModelDataJsonConstants

引入三个bean
ActModelService 为我们自己定义的业务的bean,其他两个上面已经介绍过了。

@Autowired
  private RepositoryService repositoryService;

  @Autowired
  private ObjectMapper objectMapper;

  @Autowired
  private ActModelService actModelService;

下面就是就model进行更新了, 我们需要把画的流程图定义以json的格式和流程图片转成的字节码存储到工作流中。可以使用 repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8"));
repositoryService.addModelEditorSourceExtra(model.getId(), result);

@RequestMapping(value="/model/{modelId}/save", method = RequestMethod.PUT)
  @ResponseStatus(value = HttpStatus.OK)
  public void saveModel(@PathVariable String modelId,
                        @RequestParam String name, @RequestParam String description,
                        @RequestParam String json_xml, @RequestParam String svg_xml) {

    try {
      Model model = repositoryService.getModel(modelId);

      ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());

      int newVersion = model.getVersion()+1;
      modelJson.put(MODEL_NAME, name);
      modelJson.put(MODEL_DESCRIPTION, description);
      modelJson.put(MODEL_REVISION, newVersion);

      model.setMetaInfo(modelJson.toString());
      model.setName(name);
      model.setVersion(newVersion);
      repositoryService.saveModel(model);

      repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes("utf-8"));

      InputStream svgStream = new ByteArrayInputStream(svg_xml.getBytes("utf-8"));
      TranscoderInput input = new TranscoderInput(svgStream);

      PNGTranscoder transcoder = new PNGTranscoder();
      // Setup output
      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
      TranscoderOutput output = new TranscoderOutput(outStream);

      // Do the transformation
      transcoder.transcode(input, output);
      final byte[] result = outStream.toByteArray();
      repositoryService.addModelEditorSourceExtra(model.getId(), result);
      outStream.close();

      // 更新数据库
      ActModel actModel = actModelService.getById(modelId);
      // 更新key
      String key = StrUtil.subBetween(json_xml, "\"process_id\":\"", "\",\"name\"");
      actModel.setModelKey(key);
      actModel.setName(name);
      actModel.setDescription(description);
      actModel.setVersion(newVersion);
      actModelService.updateById(actModel);
    } catch (Exception e) {
      log.error("保存模型出错", e);
      throw new ActivitiException("保存模型出错", e);
    }
  }

到此为止,我们就定义了一个工作流的模型, 这个模型包括基本的名称,key,版本号,工作流程定义,工作流程图片。现在我们只是定义了一个模型,还是无法使用。下面要把模型部署。

模型部署

下面就开始部署模型了,只有部署过的模型才会被创建一个工作流。部署过的模型,是可以在更改的,但是需要重新部署才会影响到已经部署的模型。

模型部署的接口我们可以新创建一个controller, 定义一个方法接收模型ID。

ActModelController.java

引入bean

RepositoryService 仍然是上面我们提到过的activiti自带的bean对象,ObjectMapper 同样是fastjson提供的Bean.

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    ObjectMapper objectMapper;

下面是方法接口, 我们第一步:获取模型,模型的内容,是我们在上面的方法,创建模型和保存模型内容的时候调用ACITIVITI的API,添加到ACITIVITI自带的表中的。 这里我们可以直接调用API获取出来。

第二步:将定义的流程转换成BpmnModel字节数组, 里面所有转换工具基本都是acitiviti提供的。
第三步—第五步,可以看代码中的注释。

@RequestMapping(value = "/deploy/{id}", method = RequestMethod.GET)
    @ApiOperation(value = "部署发布模型")
    public Result<Object> deploy(@PathVariable String id) {

        //第一步:  获取模型
        Model modelData = repositoryService.getModel(id);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

        if (bytes == null) {
            return ResultUtil.error("模型数据为空,请先成功设计流程并保存");
        }

        try {
//            第二步,将定义的流程转换成BpmnModel字节数组
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if(model.getProcesses().size()==0){
                return ResultUtil.error("模型不符要求,请至少设计一条主线流程");
            }
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

            //第三步: 部署发布模型流程
            String processName = modelData.getName() + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addString(processName, new String(bpmnBytes, "UTF-8"))
                    .deploy();

            // 第四步: 设置流程分类 保存扩展流程至数据库
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
            // 第五步: 将流程定义的内容保存至本地数据库中一份,主要是因为方便业务开发的时候进行查询展示管理。
            ActModel actModel = actModelService.getById(id);
            for (ProcessDefinition pd : list) {
                ActProcess actProcess = new ActProcess();
                actProcess.setId(pd.getId());
                actProcess.setName(modelData.getName());
                actProcess.setProcessKey(modelData.getKey());
                actProcess.setDeploymentId(deployment.getId());
                actProcess.setDescription(actModel.getDescription());
                actProcess.setVersion(pd.getVersion());
                actProcess.setXmlName(pd.getResourceName());
                actProcess.setDiagramName(pd.getDiagramResourceName());
                actProcessService.setAllOldByProcessKey(modelData.getKey());
                actProcess.setLatest(true);
                actProcessService.saveOrUpdate(actProcess);
            }
        }catch (Exception e){
            log.error(e.toString());
            return ResultUtil.error("部署失败");
        }

        return ResultUtil.success("部署成功");
    }

第五步我们是使用自己项目中的API将数据保存到自己创建的表中了。 下面帖出自己创建的对象,可以参照对象建表。

@Data
@TableName("t_act_process")
@ApiModel(value = "流程定义")
public class ActProcess extends TbootBaseEntity {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "流程名称")
    private String name;

    @ApiModelProperty(value = "流程标识名称")
    private String processKey;

    @ApiModelProperty(value = "版本")
    private Integer version;

    @ApiModelProperty(value = "部署id")
    private String deploymentId;

    @ApiModelProperty(value = "所属分类")
    private String categoryId;

    @ApiModelProperty(value = "xml文件名")
    private String xmlName;

    @ApiModelProperty(value = "流程图片名")
    private String diagramName;

    @ApiModelProperty(value = "描述/备注")
    private String description;

    @ApiModelProperty(value = "最新版本")
    private Boolean latest;

    @ApiModelProperty(value = "流程状态 部署后默认1激活")
    private Integer status = ActivitiConstant.PROCESS_STATUS_ACTIVE;

    @ApiModelProperty(value = "关联前端表单路由名")
    private String routeName;

    @ApiModelProperty(value = "关联业务表名")
    private String businessTable;

    @ApiModelProperty(value = "是否所有人可见")
    private Boolean allUser;

    @ApiModelProperty(value = "所属分类名称")
    private String categoryTitle;
}

流程部署是完成了,但是还有一个比较重要的工作就是,节点的审批人还没有设置。 下面我们就要开始设计节点的审批人员。

给流程节点设置审批人员

如果我们要给流程节点设置审批人员,那么首先我们要获取两个要素,第一个就是流程节点,第二个就是人员。 人员是强自定义的业务,这里就不在介绍了。 下面我们主要是聊一聊流程节点。

业务流程是通过部署流程的ID sickLeave:6:27516 来获取包括详细定义流程的BpmnModel。 这个ID是系统自动拼接的,总共分三位: 第一位:是模型KEY,我们在模型创建的时候自已添加的,第二部分是部署的版本号,当我们在部署模型的时侯这个值会累加, 第三部分是系统自动添加的。

我们获取BpmnModel之后就可以获取里面的节点了。 可以开发一个接口来专门获取里面的节点信息。

ActProcessController

	@RequestMapping(value = "/getProcessNode/{id}", method = RequestMethod.GET)
    @ApiOperation(value = "通过流程定义id获取流程节点")
    public Result<List<ProcessNodeVo>> getProcessNode(@ApiParam("流程定义id") @PathVariable String id){

        // 第一步: 获取Bpmn流程
        BpmnModel bpmnModel = repositoryService.getBpmnModel(id);

        List<ProcessNodeVo> list = new ArrayList<>();

        // 第二步: 获取BPMN中的流程
        List<Process> processes = bpmnModel.getProcesses();
        if(processes==null||processes.size()==0){
            return new ResultUtil<List<ProcessNodeVo>>().setData(null);
        }
        for(Process process : processes){
            // 对流程中的每一个节点进行遍历 获取里面的信息
            Collection<FlowElement> elements = process.getFlowElements();
            for(FlowElement element : elements){
                ProcessNodeVo node = new ProcessNodeVo();
                node.setId(element.getId());
                node.setTitle(element.getName());
                if(element instanceof StartEvent){
                    // 开始节点
                    node.setType(ActivitiConstant.NODE_TYPE_START);
                    // 设置关联用户
                    node.setUsers(actNodeService.findUserByNodeId(id));
                    // 设置关联角色
                    node.setRoles(actNodeService.findRoleByNodeId(id));
                    // 设置关联部门
                    node.setDepartments(actNodeService.findDepartmentByNodeId(id));
                }else if(element instanceof UserTask){
                    // 用户任务
                    node.setType(ActivitiConstant.NODE_TYPE_TASK);
                    // 设置关联用户
                    node.setUsers(actNodeService.findUserByNodeId(element.getId()));
                    // 设置关联角色
                    node.setRoles(actNodeService.findRoleByNodeId(element.getId()));
                    // 设置关联部门
                    node.setDepartments(actNodeService.findDepartmentByNodeId(element.getId()));
                    // 是否设置操作人负责人
                    node.setChooseDepHeader(actNodeService.hasChooseDepHeader(element.getId()));
                    // 是否设置操作人负责人
                    node.setCustomUser(actNodeService.hasCustomUser(element.getId()));
                }else if(element instanceof EndEvent){
                    // 结束
                    node.setType(ActivitiConstant.NODE_TYPE_END);
                }else{
                    // 排除其他连线或节点
                    continue;
                }
                list.add(node);
            }
        }
        return new ResultUtil<List<ProcessNodeVo>>().setData(list);
    }

这里面有两个项目中自定义的包装类,主要用于给前端包装显示使用的。

@Data
@Accessors(chain = true)
@TableName("t_act_node")
@ApiModel(value = "节点")
public class ActNode extends TbootBaseEntity {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "节点id")
    private String nodeId;

    @ApiModelProperty(value = "节点关联类型 0角色 1用户 2部门")
    private Integer type;

    @ApiModelProperty(value = "关联其他表id")
    private String relateId;

}
package com.budongfeng.tboot.modules.activiti.vo;

import com.budongfeng.tboot.modules.base.entity.Department;
import com.budongfeng.tboot.modules.base.entity.Role;
import com.budongfeng.tboot.modules.base.entity.User;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

/**
 * @author 田培融
 */
@Data
public class ProcessNodeVo {

    @ApiModelProperty(value = "节点id")
    private String id;

    @ApiModelProperty(value = "节点名")
    private String title;

    @ApiModelProperty(value = "节点类型 0开始 1用户任务 2结束 3排他网关")
    private Integer type;

    @ApiModelProperty(value = "关联角色")
    private List<Role> roles;

    @ApiModelProperty(value = "关联用户")
    private List<User> users;

    @ApiModelProperty(value = "关联部门")
    private List<Department> departments;

    @ApiModelProperty(value = "多级连续部门负责人")
    private Boolean chooseDepHeader = false;

    @ApiModelProperty(value = "自选用户")
    private Boolean customUser = false;

    @ApiModelProperty(value = "节点展开 前端所需")
    private Boolean expand = true;
}

流程的结束也是不同的,我们做了一个简单的区别。 下面就可以给节点添加用户了。 这里我们定义了两个接口,第一个修改流程定义的分类。 这里其实是可以不用配置的,只是为了代码的完善, 后续的查询。

 @RequestMapping(value = "/updateInfo", method = RequestMethod.POST)
    @ApiOperation(value = "修改关联路由表单分类或备注")
    public Result<Object> updateInfo(ActProcess actProcess){

        ActProcess old = actProcessService.getById(actProcess.getId());
        if(old==null){
            return ResultUtil.error("流程信息不存在");
        }
        ProcessDefinition pd = repositoryService.getProcessDefinition(actProcess.getId());
        if(pd==null){
            return ResultUtil.error("流程定义不存在");
        }
        PageUtil.SQLInject(actProcess.getBusinessTable());
        repositoryService.setProcessDefinitionCategory(actProcess.getId(), actProcess.getCategoryId());
        repositoryService.setDeploymentCategory(pd.getDeploymentId(), actProcess.getCategoryId());
        old.setRouteName(actProcess.getRouteName());
        old.setBusinessTable(actProcess.getBusinessTable());
        old.setDescription(actProcess.getDescription());
        if(StrUtil.isNotBlank(actProcess.getCategoryId())){
            ActCategory c = actCategoryService.getById(actProcess.getCategoryId());
            if(c!=null){
                old.setCategoryId(actProcess.getCategoryId());
                old.setCategoryTitle(c.getTitle());
            }
        }else{
            old.setCategoryId(null);
            old.setCategoryTitle("");
        }
        actProcessService.updateById(old);
        return ResultUtil.data("修改成功");
    }

关于上面的代码,repositoryService这个API的相关操作是Activiti工作流相关的,其他的是我们自己开发的。

我们可以先设置谁能发起这个流程,后面我们在设置谁能处理这个节点。 这里面的代码和工作流API没有关系 。纯属业务代码, 因此就不在详细介绍了。

    @RequestMapping(value = "/editStartUser", method = RequestMethod.POST)
    @ApiOperation(value = "编辑流程可发起用户")
    public Result<Object> editStartUser(@ApiParam("流程定义id") @RequestParam String nodeId,
                                        @ApiParam("是否所有用户可见") @RequestParam Boolean allUser,
                                        @RequestParam(required = false) String[] userIds,
                                        @RequestParam(required = false) String[] roleIds,
                                        @RequestParam(required = false) String[] departmentIds){

        // 删除其关联权限
        actNodeService.deleteByNodeId(nodeId);
        actStarterService.deleteByProcessDefId(nodeId);
        ActProcess actProcess = actProcessService.getById(nodeId);
        if(actProcess==null){
            return ResultUtil.error("流程不存在");
        }
        actProcess.setAllUser(allUser);
        actProcessService.updateById(actProcess);
        if(!allUser){
            HashSet<String> starters = new HashSet<>();
            List<ActStarter> listStarter = new ArrayList<>();
            List<ActNode> listNode = new ArrayList<>();
            // 分配新用户
            if(userIds!=null){
                for(String userId : userIds){
                    // 保存用户
                    if(!starters.contains(userId)){
                        ActStarter actStarter = new ActStarter().setProcessDefId(nodeId).setUserId(userId);
                        listStarter.add(actStarter);
                        starters.add(userId);
                    }
                    // 保存配置
                    ActNode actNode = new ActNode().setNodeId(nodeId).setRelateId(userId).setType(ActivitiConstant.NODE_USER);
                    listNode.add(actNode);
                }
            }
            // 分配新角色的用户
            if(roleIds!=null){
                for(String roleId : roleIds){
                    // 通过角色获取用户
                    List<UserRole> userRoles = userRoleService.findByRoleId(roleId);
                    for (UserRole ur : userRoles){
                        // 保存用户
                        if(!starters.contains(ur.getUserId())){
                            ActStarter actStarter = new ActStarter().setProcessDefId(nodeId).setUserId(ur.getUserId());
                            listStarter.add(actStarter);
                            starters.add(ur.getUserId());
                        }
                    }
                    ActNode actNode = new ActNode().setNodeId(nodeId).setRelateId(roleId).setType(ActivitiConstant.NODE_ROLE);
                    listNode.add(actNode);
                }
            }
            // 分配新部门
            if(departmentIds!=null){
                for(String departmentId : departmentIds){
                    // 通过部门获取用户
                    List<User> users = userService.findByDepartmentId(departmentId);
                    for (User u : users){
                        // 保存用户
                        if(!starters.contains(u.getId())){
                            ActStarter actStarter = new ActStarter().setProcessDefId(nodeId).setUserId(u.getId());
                            listStarter.add(actStarter);
                            starters.add(u.getId());
                        }
                    };
                    ActNode actNode = new ActNode().setNodeId(nodeId).setRelateId(departmentId).setType(ActivitiConstant.NODE_DEPARTMENT);
                    listNode.add(actNode);
                }
            }
            // 批量保存
            actStarterService.saveOrUpdateBatch(listStarter);
            actNodeService.saveOrUpdateBatch(listNode);
            // GC
            starters = null;
        }
        return ResultUtil.success("操作成功");
    }

    @RequestMapping(value = "/editNodeUser", method = RequestMethod.POST)
    @ApiOperation(value = "编辑节点分配用户")
    public Result<Object> editNodeUser(@RequestParam String nodeId,
                                       @RequestParam(required = false) String[] userIds,
                                       @RequestParam(required = false) String[] roleIds,
                                       @RequestParam(required = false) String[] departmentIds,
                                       @ApiParam("是否勾选操连续多级部门负责人") @RequestParam(required = false) Boolean chooseDepHeader,
                                       @ApiParam("是否勾选用户自选") @RequestParam(required = false) Boolean customUser){

        // 删除其关联数据
        actNodeService.deleteByNodeId(nodeId);
        if(customUser!=null&&customUser){
            ActNode actNode = new ActNode().setNodeId(nodeId).setType(ActivitiConstant.NODE_CUSTOM);
            actNodeService.save(actNode);
            return ResultUtil.success("操作成功");
        }
        List<ActNode> list = new ArrayList<>();
        // 分配新用户
        if(userIds!=null){
            for(String userId : userIds){
                ActNode actNode = new ActNode().setNodeId(nodeId).setRelateId(userId).setType(ActivitiConstant.NODE_USER);
                list.add(actNode);
            }
        }
        // 分配新角色
        if(roleIds!=null){
            for(String roleId : roleIds){
                ActNode actNode = new ActNode().setNodeId(nodeId).setRelateId(roleId).setType(ActivitiConstant.NODE_ROLE);
                list.add(actNode);
            }
        }
        // 分配新部门
        if(departmentIds!=null){
            for(String departmentId : departmentIds){
                ActNode actNode = new ActNode().setNodeId(nodeId).setRelateId(departmentId).setType(ActivitiConstant.NODE_DEPARTMENT);
                list.add(actNode);
            }
        }
        if(chooseDepHeader!=null&&chooseDepHeader){
            ActNode actNode = new ActNode().setNodeId(nodeId).setType(ActivitiConstant.NODE_DEP_HEADER);
            list.add(actNode);
        }
        // 批量保存
        actNodeService.saveOrUpdateBatch(list);
        return ResultUtil.success("操作成功");
    }

到此工作流的会签算是完成了。 部署完成了,下面我会在写一篇文章来介绍,关于启动一个工作流,到工作流结束的流程。还是会使用这个会签的案例。

希望和你交个朋友

Activiti工作流会签一 部署流程_第2张图片

你可能感兴趣的:(工作流,activiti,工作流,会签)