成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Springboot整合Camunda工作流引擎實(shí)現(xiàn)審批流程實(shí)例

開(kāi)發(fā) 前端
這里我不通過(guò)上面的rest api 進(jìn)行部署,而是通過(guò)自定義的接口然后調(diào)用camunda的相關(guān)api來(lái)實(shí)現(xiàn)流程部署。

環(huán)境:Spingboot2.6.14 +
camunda-spring-boot-starter7.18.0

環(huán)境配置

依賴(lài)配置

<camunda.version>7.18.0</camunda.version>
<dependency>
  <groupId>org.camunda.bpm.springboot</groupId>
  <artifactId>camunda-bpm-spring-boot-starter-webapp</artifactId>
  <version>${camunda.version}</version>
</dependency>
<dependency>
  <groupId>org.camunda.bpm.springboot</groupId>
  <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
  <version>${camunda.version}</version>
</dependency>

應(yīng)用程序配置

camunda.bpm:
  webapp:
    # 設(shè)置管理控制臺(tái)的訪(fǎng)問(wèn)上下文
    application-path: /workflow
  auto-deployment-enabled: true
  admin-user:
    # 配置登錄管理控制臺(tái)的用戶(hù)
    id: admin
    password: admin
    firstName: admin
  filter:
    create: All tasks
  database:
    #數(shù)據(jù)庫(kù)類(lèi)型
    type: mysql 
    #是否自動(dòng)更新表信息
    schema-update: true
logging:
  level:
    #配置日志,這樣在開(kāi)發(fā)過(guò)程中就能看到每步執(zhí)行的SQL語(yǔ)句了
    '[org.camunda.bpm.engine.impl.persistence.entity]': debug
---
spring:
  jersey:
    application-path: /api-flow
    type: servlet
    servlet:
      load-on-startup: 0

通過(guò)上面的配置后訪(fǎng)問(wèn)控制臺(tái):http://localhost:8100/workflow/

圖片

默認(rèn)是沒(méi)有上面的tasks中的內(nèi)容,這里是我之前測(cè)試數(shù)據(jù)

環(huán)境準(zhǔn)備好后,接下來(lái)就可以設(shè)計(jì)工作流程。

上面的camunda-bpm-spring-boot-starter-rest依賴(lài)中定義了一系列操作camunda的 rest api 這api的實(shí)現(xiàn)是通過(guò)jersey實(shí)現(xiàn),我們可以通過(guò)/api-flow前綴來(lái)訪(fǎng)問(wèn)這些接口,具體有哪些接口,我們可以通過(guò)官方提供的camunda-bpm-run-7.18.0.zip

http://localhost:8080/swaggerui/#/

設(shè)計(jì)流程

這里設(shè)計(jì)兩個(gè)節(jié)點(diǎn)的審批流程,經(jīng)理審批---》人事審批 流程。

圖片

經(jīng)理審批節(jié)點(diǎn)

圖片

人事審批節(jié)點(diǎn)

上面配置了2個(gè)用戶(hù)任務(wù)節(jié)點(diǎn),并且為每個(gè)任務(wù)節(jié)點(diǎn)都設(shè)置了表達(dá)式,指定節(jié)點(diǎn)的審批人。

最終生成的流程XML內(nèi)容如下:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="sample-diagram" targetNamespace="http://pack.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
  <bpmn2:process id="Process_1" isExecutable="true">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>Flow_18pxcpx</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:sequenceFlow id="Flow_18pxcpx" sourceRef="StartEvent_1" targetRef="Activity_0vs8hu4" />
    <bpmn2:userTask id="Activity_0vs8hu4" camunda:assignee="${uid}">
      <bpmn2:incoming>Flow_18pxcpx</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0n014x3</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:sequenceFlow id="Flow_0n014x3" sourceRef="Activity_0vs8hu4" targetRef="Activity_0bcruuz" />
    <bpmn2:userTask id="Activity_0bcruuz" camunda:assignee="${mid}">
      <bpmn2:incoming>Flow_0n014x3</bpmn2:incoming>
      <bpmn2:outgoing>Flow_0dsfy6s</bpmn2:outgoing>
    </bpmn2:userTask>
    <bpmn2:endEvent id="Event_1xosttx">
      <bpmn2:incoming>Flow_0dsfy6s</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="Flow_0dsfy6s" sourceRef="Activity_0bcruuz" targetRef="Event_1xosttx" />
  </bpmn2:process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
      <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
        <dc:Bounds x="252" y="252" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_1py8b5e_di" bpmnElement="Activity_0vs8hu4">
        <dc:Bounds x="340" y="230" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Activity_0arbs87_di" bpmnElement="Activity_0bcruuz">
        <dc:Bounds x="500" y="230" width="100" height="80" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="Event_1xosttx_di" bpmnElement="Event_1xosttx">
        <dc:Bounds x="662" y="252" width="36" height="36" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="Flow_18pxcpx_di" bpmnElement="Flow_18pxcpx">
        <di:waypoint x="288" y="270" />
        <di:waypoint x="340" y="270" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0n014x3_di" bpmnElement="Flow_0n014x3">
        <di:waypoint x="440" y="270" />
        <di:waypoint x="500" y="270" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="Flow_0dsfy6s_di" bpmnElement="Flow_0dsfy6s">
        <di:waypoint x="600" y="270" />
        <di:waypoint x="662" y="270" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</bpmn2:definitions>

部署流程

這里我不通過(guò)上面的rest api 進(jìn)行部署,而是通過(guò)自定義的接口然后調(diào)用camunda的相關(guān)api來(lái)實(shí)現(xiàn)流程部署。

上面的流程設(shè)計(jì)我是通過(guò)vue整合的camunda進(jìn)行設(shè)計(jì),并沒(méi)有使用官方提供的設(shè)計(jì)器。設(shè)計(jì)完成后直接上傳到服務(wù)端。

接口

@RestController
@RequestMapping("/camunda")
public class BpmnController {


  // 上傳路徑
  @Value("${gx.camunda.upload}")
  private String path ;
  
  // 通用的工作流操作api服務(wù)類(lèi)
  @Resource
  private ProcessService processService ;
  
  @PostMapping("/bpmn/upload")
  public AjaxResult uploadFile(MultipartFile file, String fileName, String name) throws Exception {
    try {
      // 上傳并返回新文件名稱(chēng)
      InputStream is = file.getInputStream() ;
      File storageFile = new File(path + File.separator + fileName) ;
      FileOutputStream fos = new FileOutputStream(new File(path + File.separator + fileName)) ;
      byte[] buf = new byte[10 * 1024] ;
      int len = -1 ;
      while((len = is.read(buf)) > -1) {
        fos.write(buf, 0, len) ;
      }
      fos.close() ;
      is.close() ;
      // 創(chuàng)建部署流程
      processService.createDeploy(fileName, name, new FileSystemResource(storageFile)) ;
      return AjaxResult.success();
    } catch (Exception e) {
      return AjaxResult.error(e.getMessage());
    }
  }
}

部署流程Service

// 這個(gè)是camunda spring boot starter 自動(dòng)配置
@Resource
private RepositoryService repositoryService ;


public void createDeploy(String resourceName, String name, org.springframework.core.io.Resource resource) {
  try {
    Deployment deployment = repositoryService.createDeployment()
      .addInputStream(resourceName, resource.getInputStream())
      .name(name)
      .deploy();
    logger.info("流程部署id: {}", deployment.getId());
    logger.info("流程部署名稱(chēng): {}", deployment.getName());
  } catch (IOException e) {
    throw new RuntimeException(e) ;
  }
}

執(zhí)行上面的接口就能將上面設(shè)計(jì)的流程部署到camunda中(其實(shí)就是將流程文件保存到了數(shù)據(jù)庫(kù)中,對(duì)應(yīng)的數(shù)據(jù)表是:act_ge_bytearray)。

啟動(dòng)流程

啟動(dòng)流程還是一樣,通過(guò)我們自己的接口來(lái)實(shí)現(xiàn)。

接口

@RestController
@RequestMapping("/process")
public class ProcessController {


  @Resource
  private ProcessService processService ;
  
  // 根據(jù)流程定義id,啟動(dòng)流程;整個(gè)流程需要?jiǎng)討B(tài)傳2個(gè)參數(shù)(審批人),如果不傳將會(huì)報(bào)錯(cuò)
  @GetMapping("/start/{processDefinitionId}")
  public AjaxResult startProcess(@PathVariable("processDefinitionId") String processDefinitionId) {
    Map<String, Object> variables = new HashMap<>() ;
    variables.put("uid", "1") ;
    variables.put("mid", "1000") ;
    processService.startProcessInstanceAssignVariables(processDefinitionId, "AKF", variables) ;
    return AjaxResult.success("流程啟動(dòng)成功") ;
  }
}

服務(wù)Service接口

@Resource
private RuntimeService runtimeService ;


public ProcessInstance startProcessInstanceAssignVariables(String processDefinitionId, String businessKey, Map<String, Object> variables) {
  ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, businessKey, variables);
  logger.info("流程定義ID: {}", processInstance.getProcessDefinitionId());
  logger.info("流程實(shí)例ID: {}", processInstance.getId());
  logger.info("BussinessKey: {}", processInstance.getBusinessKey()) ;
  return processInstance ;
}

流程啟動(dòng)后就可以查看當(dāng)前需要自己審批的所有審批單

圖片


接口實(shí)現(xiàn)

@Resource
private TaskService taskService ;
@Resource
private ManagementService managementService ;
// 根據(jù)時(shí)間段查詢(xún)
public List<Task> queryTasksByBusinessAndCreateTime(String assignee, String businessKey, String startTime, String endTime) {
  NativeTaskQuery nativeQuery = taskService.createNativeTaskQuery() ;
  nativeQuery.sql("select distinct RES.* from " + managementService.getTableName(TaskEntity.class) +  " RES "
                  + " left join " + managementService.getTableName(IdentityLinkEntity.class) + " I on I.TASK_ID_ = RES.ID_ "
                  + " WHERE (RES.ASSIGNEE_ = #{assignee} or "
                  + " (RES.ASSIGNEE_ is null and I.TYPE_ = 'candidate' "
                  + " and (I.USER_ID_ = #{assignee} or I.GROUP_ID_ IN ( #{assignee} ) ))) "
                  + " and RES.CREATE_TIME_ between #{startTime} and #{endTime} "
                  + " order by RES.CREATE_TIME_ asc LIMIT #{size} OFFSET 0") ;
  nativeQuery.parameter("assignee", assignee) ;
  nativeQuery.parameter("startTime", startTime) ;
  nativeQuery.parameter("endTime", endTime) ;
  nativeQuery.parameter("size", Integer.MAX_VALUE) ;
  return nativeQuery.list() ;
}

審批流程

流程啟動(dòng)后,接下來(lái)就是各個(gè)用戶(hù)任務(wù)節(jié)點(diǎn)配置的用戶(hù)進(jìn)行審批

接口

@GetMapping("/approve/{id}")
public AjaxResult approve(@PathVariable("id") String instanceId) {
  if (StringUtils.isEmpty(instanceId)) {
    return AjaxResult.error("未知審批任務(wù)") ;
  }
  // 下面的參數(shù)信息應(yīng)該自行保存管理(與發(fā)起審批設(shè)置的指派人要一致)
  Map<String, Object> variables = new HashMap<>() ;
  // 第一個(gè)節(jié)點(diǎn)所要提供的遍歷信息(這里就是依次類(lèi)推,mid等)
  variables.put("uid", "1") ;
  processService.executionTask(variables, instanceId, task -> {}, null) ;
  return AjaxResult.success() ; 
}

服務(wù)Service接口

@Resource
private TaskService taskService ;
@Resource
private RuntimeService runtimeService ;


@Transactional
public void executionTask(Map<String, Object> variables, String instanceId, Consumer<Task> consumer, String type) {
  Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult() ;
  if (task == null) {
    logger.error("任務(wù)【{}】不存在", instanceId) ;
    throw new RuntimeException("任務(wù)【" + instanceId + "】不存在") ;
  }
  taskService.setVariables(task.getId(), variables);
  taskService.complete(task.getId(), variables) ;
  long count = runtimeService.createExecutionQuery().processInstanceId(instanceId).count();
  if (count == 0) {
    consumer.accept(task) ;
  }
}

以上就完成了從整個(gè)流程的生命周期:

設(shè)計(jì)流程 ---》部署流程 ---》啟動(dòng)流程 ---》審批流程

完畢!!!

責(zé)任編輯:武曉燕 來(lái)源: 實(shí)戰(zhàn)案例錦集
相關(guān)推薦

2023-07-05 09:48:44

Activiti部署

2024-03-26 08:08:08

SpringBPMN模型

2021-12-17 08:39:39

SpringbootActiviti網(wǎng)關(guān)路由

2013-04-23 10:28:08

IBeamMDAAWF

2021-10-14 11:34:05

技術(shù)工作流引擎

2025-04-27 03:00:00

Spring流程業(yè)務(wù)

2023-01-04 08:02:16

工作流架構(gòu)設(shè)計(jì)

2011-12-14 09:58:58

JavajBPM

2015-07-14 09:26:28

微型工作流引擎設(shè)計(jì)

2024-10-17 08:39:32

2023-08-02 18:48:23

Flowable工作流引擎

2009-06-11 14:43:34

jbpm工作流引擎jBPM搭建

2009-09-01 18:26:23

C#工作流引擎

2023-03-26 00:53:04

camunda7camunda8流程引擎

2009-03-03 09:13:36

工作流BPM業(yè)務(wù)流程

2010-11-26 10:59:28

SharePoint

2014-07-31 17:03:12

2009-06-11 14:33:11

jbpm工作流引擎什么是jbpm

2021-03-12 06:44:09

Argo Workfl開(kāi)源項(xiàng)目

2025-05-14 03:20:00

AgenticAIMCP
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美精品一级 | 精品欧美乱码久久久久久1区2区 | 久久1区| 特级做a爰片毛片免费看108 | 51ⅴ精品国产91久久久久久 | 久久精品国产a三级三级三级 | 成人精品| 天天干天天操天天看 | www.婷婷亚洲基地 | 岛国av在线免费观看 | 久久久久久久综合 | 国产精彩视频 | 欧美精品久久 | 自拍视频在线观看 | 久久久婷婷 | 久在线 | 国产精品久久久久久久久免费丝袜 | 免费看日韩视频 | 成人免费大片黄在线播放 | 欧美日韩视频在线播放 | 欧美在线二区 | 国产a级毛毛片 | 手机在线一区二区三区 | 97caoporn国产免费人人 | 亚洲综合一区二区三区 | 四虎永久免费在线 | 亚洲黄色av网站 | 国产精品久久一区二区三区 | 99re视频这里只有精品 | 91影库| 午夜电影福利 | 亚洲国产精品久久久 | 中文字幕亚洲区一区二 | 在线免费av电影 | 国产精品免费一区二区 | 久久久www成人免费精品 | 久久精品69 | 一区二区三区观看视频 | 不卡视频在线 | 成人福利网 | 欧美成人在线影院 |