【弄nèng - Activiti6】应用篇之流程管理

124 阅读5分钟

文章目录

本文建议你熟悉Activiti之后在阅读,是Activiti的应用篇。直接上应用代码,包含流程查询,读取资源,部署流程文件,转模型,启动流程实例,激活/挂起,删除等功能。Springboot搭建Activiti整合流程设计器 请查看这篇博客https://blog.csdn.net/yy756127197/article/details/101211510

后台服务基于Springboot2 + Mybatis-plus3 + Lombok + Swagger + Hutool工具类

1. ActReProcdefController控制类

import cn.hutool.json.JSONUtil;
import com.it.cloud.common.annotation.SysLog;
import com.it.cloud.common.base.Result;
import com.it.cloud.common.exceptions.YYException;
import com.it.cloud.common.utils.PageUtils;
import com.it.cloud.modules.activiti.service.IActReProcdefService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

/**
 * <p>
 * 流程定义控制类
 * </p>
 *
 * @author 司马缸砸缸了
 * @since 2019-07-24
 */
@Api(value = "流程定义控制器", tags = "流程定义")
@Slf4j
@RestController
@RequestMapping("/act/procdef")
public class ActReProcdefController {


    @Autowired
    private IActReProcdefService actReProcdefService;

    @ApiOperation(value = "分页查询接口", notes = "条件,分页查询")
    @GetMapping("/page")
    public Result list(@RequestParam Map<String, Object> params) {
        log.info("分页查询所有流程定义接口,参数:{}", JSONUtil.toJsonStr(params));
        PageUtils page = actReProcdefService.queryPage(params);
        System.out.println(JSONUtil.toJsonStr(page));
        return Result.ok(page);
    }

    @ApiOperation(value = "读取资源", notes = "读取资源,通过ProcessDefinitionId")
    @GetMapping("/read")
    public void resourceRead(String id,
                             @RequestParam(required = false) String proInsId,
                             String type,
                             HttpServletResponse response) {
        log.info("读取资源, processDefinitionId:{}, proInsId:{}, type:{}", id, proInsId, type);
        InputStream resourceAsStream = actReProcdefService.readResource(id, proInsId, type);
        byte[] b = new byte[1024];
        int len = -1;
        int lenEnd = 1024;
        while (true) {
            try {
                if (!((len = resourceAsStream.read(b, 0, lenEnd)) != -1)) break;
                response.getOutputStream().write(b, 0, len);
            } catch (IOException e) {
                throw new YYException("读取资源文件失败", e);
            }
        }
    }

    @ApiOperation(value = "部署流程文件", notes = "部署流程文件")
    @SysLog("部署流程文件")
    @PostMapping("/deploy")
    @RequiresPermissions("act:reprocdef:deploy")
    public Result deploy(MultipartFile file) {
        Result result = Result.ok();
        String exportDir = this.getClass().getResource("/").getPath();
        String fileName = file.getOriginalFilename();
        if (StringUtils.isBlank(fileName)) {
            throw new YYException("请选择要部署的流程文件");
        } else {
            result = actReProcdefService.deploy(exportDir, file);
        }
        return result;
    }

    @ApiOperation(value = "转模型", notes = "转模型")
    @SysLog("转模型")
    @GetMapping("/convertToModel")
    @RequiresPermissions("act:reprocdef:convertToModel")
    public Result convertToModel(String id) {
        log.info("转模型, processDefinitionId:{}", id);
        try {
            actReProcdefService.convertToModel(id);
        } catch (Exception e) {
            return Result.error(e.getMessage());
        }
        return Result.ok();
    }

    @ApiOperation(value = "启动流程实例", notes = "启动流程实例,通过processDefinitionId")
    @SysLog("启动流程实例")
    @GetMapping("/startProcessInstance")
    @RequiresPermissions("act:reprocdef:start")
    public Result startProcessInstanceById(String processDefinitionId) {
        log.info("启动流程实例, processDefinitionId:{}", processDefinitionId);
        actReProcdefService.startProcessInstanceById(processDefinitionId);

        return Result.ok();
    }

    @ApiOperation(value = "激活/挂起", notes = "激活/挂起")
    @SysLog("激活/挂起")
    @PutMapping("/status")
    @RequiresPermissions("act:reprocdef:update")
    public Result update(String id, Integer state) {
        log.info("激活/挂起流程定义, id:{}, state:{}", id, state);
        Result result = actReProcdefService.updateState(id, state);

        return result;
    }

    @ApiOperation(value = "删除流程定义", notes = "删除流程定义(部署)")
    @SysLog("删除流程定义")
    @PostMapping("/delete")
    @RequiresPermissions("act:reprocdef:delete")
    public Result delete(@RequestBody String[] deploymentIds) {
        log.info("删除流程定义, 参数:{}", JSONUtil.toJsonStr(deploymentIds));
        actReProcdefService.deleteBatch(deploymentIds);

        return Result.ok();
    }
}

2. ActReProcdefServiceImpl服务类

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import com.it.cloud.common.base.Result;
import com.it.cloud.common.constants.ActivitiConstant;
import com.it.cloud.common.exceptions.YYException;
import com.it.cloud.common.utils.PageUtils;
import com.it.cloud.common.utils.Query;
import com.it.cloud.modules.activiti.entity.ActReProcdefEntity;
import com.it.cloud.modules.activiti.mapper.ActReProcdefMapper;
import com.it.cloud.modules.activiti.service.IActReProcdefService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.*;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 司马缸砸缸了
 * @since 2019-08-23
 */
@Slf4j
@Service
public class ActReProcdefServiceImpl extends ServiceImpl<ActReProcdefMapper, ActReProcdefEntity> implements IActReProcdefService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private RepositoryService repositoryService;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        String key = (String) params.get("key");
        String name = (String) params.get("name");
        int curPage = Integer.parseInt((String) params.get("page"));
        int limit = Integer.parseInt((String) params.get("limit"));

        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .latestVersion()
                .orderByProcessDefinitionKey()
                .asc();

        if (StringUtils.isNotBlank(name)) {
            processDefinitionQuery.processDefinitionNameLike("%" + name + "%");
        }
        if (StringUtils.isNotBlank(key)) {
            processDefinitionQuery.processDefinitionKey(key);
        }
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage((curPage - 1) * limit, limit);
        /**
         * 转换类型
         */
        List<ActReProcdefEntity> list = processDefinitionList.stream()
                .map(item -> convertObject(item))
                .collect(Collectors.toList());
        Page<ActReProcdefEntity> page = new Query<ActReProcdefEntity>(params).getPage();
        page.setTotal(processDefinitionQuery.count());
        page.setRecords(list);

        return new PageUtils(page);
    }

    @Override
    public InputStream readResource(String id, String proInsId, String type) {
        if (StringUtils.isBlank(id)) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(proInsId)
                    .singleResult();
            id = processInstance.getProcessDefinitionId();
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(id)
                .singleResult();

        String resourceName = "";
        if (ActivitiConstant.IMAGE.equals(type)) {
            resourceName = processDefinition.getDiagramResourceName();
        } else if (ActivitiConstant.XML.equals(type)) {
            resourceName = processDefinition.getResourceName();
        }
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);

        return resourceAsStream;
    }

    @Override
    public Result deploy(String exportDir, MultipartFile file) {
        String fileName = file.getOriginalFilename();

        InputStream fileInputStream = null;
        try {
            fileInputStream = file.getInputStream();
        } catch (IOException e) {
            throw new YYException("上传文件部署失败", e);
        }
        Deployment deployment = null;
        String extension = FilenameUtils.getExtension(fileName);
        if (ActivitiConstant.ZIP.equals(extension) || ActivitiConstant.BAR.equals(extension)) {
            ZipInputStream zip = new ZipInputStream(fileInputStream);
            deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
        } else if (ActivitiConstant.PNG.equals(extension)) {
            deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
        } else if (fileName.indexOf(ActivitiConstant.BPMN20) != -1) {
            deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
        } else if (ActivitiConstant.BPMN.equals(extension)) {
            deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
        } else {
            return Result.error("不支持的文件类型:" + extension);
        }

        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
        if (CollectionUtil.isEmpty(list)) {
            return Result.error("部署失败,没有流程");
        }
        // 设置流程分类(可选)
        for (ProcessDefinition processDefinition : list) {
            repositoryService.setProcessDefinitionCategory(processDefinition.getId(), processDefinition.getCategory());
            log.info("部署成功,流程ID:{}", processDefinition.getId());
        }

        return Result.ok();
    }

    @Override
    public Model convertToModel(String id) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
        InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = null;
        XMLStreamReader xtr = null;
        try {
            in = new InputStreamReader(bpmnStream, "UTF-8");
            xtr = xif.createXMLStreamReader(in);
        } catch (Exception e) {
            throw new YYException("转模型失败", e);
        }

        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        BpmnJsonConverter converter = new BpmnJsonConverter();
        ObjectNode modelNode = converter.convertToJson(bpmnModel);
        // Model 实体
        Model modelData = repositoryService.newModel();
        modelData.setKey(processDefinition.getKey());
        modelData.setName(processDefinition.getName());
        modelData.setCategory(processDefinition.getCategory());
        modelData.setDeploymentId(processDefinition.getDeploymentId());
        modelData.setVersion(Integer.parseInt(String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));
        // 元数据
        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());

        repositoryService.saveModel(modelData);
        try {
            repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            throw new YYException("转模型失败", e);
        }

        return modelData;
    }

    @Override
    public void startProcessInstanceById(String processDefinitionId) {
        runtimeService.startProcessInstanceById(processDefinitionId);
    }

    @Override
    public Result updateState(String id, Integer state) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(id)
                .singleResult();
        if (state == ActivitiConstant.ONE) {
            // 激活
            if (!processDefinition.isSuspended()) {
                return Result.error("流程状态不能激活");
            }
            repositoryService.activateProcessDefinitionById(id, true, DateUtil.date());
            log.info("已激活ID为[" + id + "]的流程定义");
        } else if (state == ActivitiConstant.TWO) {
            // 挂起
            if (processDefinition.isSuspended()) {
                return Result.error("流程状态不能挂起");
            }
            repositoryService.suspendProcessDefinitionById(id, true, DateUtil.date());
            log.info("已挂起ID为[" + id + "]的流程定义");
        }

        return Result.ok();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteBatch(String[] deploymentIds) {
        // false 不带级联的删除
        // 只能删除没有启动的流程,如果流程启动,就会抛出异常
        //
        // true 能级联的删除
        // 能删除启动的流程,会删除和当前规则相关的所有信息,正在执行的信息,也包括历史信息

        Arrays.stream(deploymentIds).forEach(deploymentId -> {
            repositoryService.deleteDeployment(deploymentId, false);
        });
    }

    /**
     * 转换类型
     *
     * @param processDefinition
     * @return
     */
    private ActReProcdefEntity convertObject(ProcessDefinition processDefinition) {
        ActReProcdefEntity entity = new ActReProcdefEntity();
        String deploymentId = processDefinition.getDeploymentId();
        Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
        entity.setId(processDefinition.getId());
        entity.setKey(processDefinition.getKey());
        entity.setName(processDefinition.getName());
        entity.setDeployTime(deployment == null ? null : DateUtil.date(deployment.getDeploymentTime()).toTimestamp());
        entity.setDeploymentId(processDefinition.getDeploymentId());
        entity.setSuspensionState(processDefinition.isSuspended() ? ActivitiConstant.TWO : ActivitiConstant.ONE);
        entity.setResourceName(processDefinition.getResourceName());
        entity.setDgrmResourceName(processDefinition.getDiagramResourceName());
        entity.setCategory(processDefinition.getCategory());
        entity.setVersion(processDefinition.getVersion());
        entity.setDescription(processDefinition.getDescription());
        entity.setEngineVersion(processDefinition.getEngineVersion());
        entity.setTenantId(processDefinition.getTenantId());

        return entity;
    }
}

3. 实体类

对应Activiti数据表ACT_RE_PROCDEF

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 *
 * </p>
 *
 * @author 司马缸砸缸了
 * @since 2019-08-23
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("ACT_RE_PROCDEF")
public class ActReProcdefEntity implements Serializable {


    @ApiModelProperty(value = "流程ID,由《流程编号:流程版本号:自增长ID》组成")
    @TableId(value = "ID_")
    private String id;

    @ApiModelProperty(value = "乐观锁版本号")
    @TableField("REV_")
    private Integer rev;

    @ApiModelProperty(value = "流程命名空间(该编号就是流程文件targetNamespace的属性值)")
    @TableField("CATEGORY_")
    private String category;

    @ApiModelProperty(value = "流程名称(该编号就是流程文件process元素的name属性值)")
    @TableField("NAME_")
    private String name;

    @ApiModelProperty(value = "流程编号(该编号就是流程文件process元素的id属性值)")
    @TableField("KEY_")
    private String key;

    @ApiModelProperty(value = "流程版本号(由程序控制,新增即为1,修改后依次加1来完成的)")
    @TableField("VERSION_")
    private Integer version;

    @ApiModelProperty(value = "部署编号")
    @TableField("DEPLOYMENT_ID_")
    private String deploymentId;

    @ApiModelProperty(value = "资源文件名称")
    @TableField("RESOURCE_NAME_")
    private String resourceName;

    @ApiModelProperty(value = "图片资源文件名称")
    @TableField("DGRM_RESOURCE_NAME_")
    private String dgrmResourceName;

    @ApiModelProperty(value = "描述信息")
    @TableField("DESCRIPTION_")
    private String description;

    @ApiModelProperty(value = "是否从key启动,start节点是否存在formKey,0否  1是")
    @TableField("HAS_START_FORM_KEY_")
    private String hasStartFormKey;

    @ApiModelProperty(value = "是否有图片预览")
    @TableField("HAS_GRAPHICAL_NOTATION_")
    private String hasGraphicalNotation;

    @ApiModelProperty(value = "是否挂起,1激活 2挂起")
    @TableField("SUSPENSION_STATE_")
    private Integer suspensionState;

    @ApiModelProperty(value = "所属系统")
    @TableField("TENANT_ID_")
    private String tenantId;

    @ApiModelProperty(value = "工作流引擎版本")
    @TableField("ENGINE_VERSION_")
    private String engineVersion;

    @ApiModelProperty(value = "部署时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(exist = false)
    private Timestamp deployTime;

}

源码地址

IT-CLOUD :IT服务管理平台,集成基础服务,中间件服务,监控告警服务等。\