스프링부트와 Flowable을 활용한 워크플로우 구현 방법

최근 진행한 프로젝트에서 승인 모듈을 구현해야 했는데, 이를 위해 Flowable을 선택했습니다.

아래는 Flowable의 기본 사용 방법에 대한 소개입니다.

프로세스 구현

1. 의존성 추가

<!-- Flowable 의존성 -->
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.7.2</version>
</dependency>

2. 프로세스 다이어그램 생성

여기서는 IDEA의 Flowable BPMN visualizer 플러그인을 사용합니다.

bpmn20.xml 파일 생성

플러그인을 이용한 프로세스 다이어그램 작성

  1. bpmn20.xml 파일을 우클릭합니다
  2. 플러그인을 사용하여 프로세스를 시각화합니다
  3. 필요한 요소들을 다이어그램에 추가합니다

다이어그램 작성이 완료되면, bpmn20.xml 파일에 내용이 자동으로 생성됩니다.

컨트롤러 구현

package com.example.workflow.controller;

import com.example.workflow.common.ResultVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Task;
import org.flowable.engine.*;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 휴가 승인 컨트롤러
 */
@RestController
@RequestMapping("/approval")
@Slf4j
@Api(value = "휴가 승인 API", tags = "휴가 승인 API")
public class ApprovalController {
    
    @Autowired
    private RuntimeService runtimeService;
    
    @Autowired
    private TaskService taskService;

    /**
     * 휴가 승인 프로세스 생성
     * @param employeeId 직원 ID
     * @param employeeName 직원 이름
     * @param leaveDays 휴가 일수
     * @param reason 휴가 사유
     * @return 생성 결과
     */
    @PostMapping("create")
    @ApiOperation("휴가 승인 프로세스 생성")
    public ResultVo<String> createApprovalRequest(
            @RequestParam("employeeId") Integer employeeId,
            @RequestParam("employeeName") String employeeName,
            @RequestParam("leaveDays") Integer leaveDays,
            @RequestParam("reason") String reason) {
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("employeeId", employeeId);
        variables.put("employeeName", employeeName);
        variables.put("leaveDays", leaveDays);
        variables.put("reason", reason);
        
        // 프로세스 인스턴스 시작
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveApproval", variables);
        
        log.info("프로세스 변수: {}", variables);
        return ResultVo.success("휴가 신청이 접수되었습니다. 프로세스 ID: " + processInstance.getId());
    }

    /**
     * 프로세스 다이어그램 조회
     * @param response HTTP 응답 객체
     * @param processId 프로세스 ID
     * @throws 예외 처리
     */
    @GetMapping("diagram")
    @ApiOperation("프로세스 다이어그램 조회")
    public void getProcessDiagram(HttpServletResponse response, @RequestParam("processId") String processId) throws Exception {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processId)
                .singleResult();

        if (processInstance == null) {
            return;
        }
        
        Task currentTask = taskService.createTaskQuery()
                .processInstanceId(processInstance.getId())
                .singleResult();
        
        // 현재 실행 중인 작업 조회
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(currentTask.getId())
                .list();
    }

    /**
     * 휴가 승인 처리
     * @param taskId 작업 ID
     * @param approvalDecision 승인 여부 (true: 승인, false: 거절)
     * @return 처리 결과
     */
    @PostMapping("decision")
    @ApiOperation("휴가 승인/거절 처리")
    public ResultVo<String> processApprovalDecision(
            @RequestParam("taskId") String taskId,
            @RequestParam("approvalDecision") Boolean approvalDecision) {
        
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            return ResultVo.success("존재하지 않는 승인 요청입니다");
        }
        
        // 승인 변수 설정
        Map<String, Object> variables = new HashMap<>();
        variables.put("approvalResult", approvalDecision);
        
        // 작업 완료 처리
        taskService.complete(taskId, variables);
        
        return ResultVo.success("승인 처리가 완료되었습니다. 결과: " + (approvalDecision ? "승인" : "거절"));
    }
}

태그: 스프링부트 Flowable BPMN 워크플로우 자동화 승인 시스템

5월 23일 00:03에 게시됨