Skip to content

getBpmnModel

Flowable 7.1.0 摘要:获取流程定义的BPMN模型对象,包含流程的完整结构信息。

方法签名与说明

BpmnModel getBpmnModel(String processDefinitionId)

根据流程定义ID获取对应的BPMN模型对象。BPMN模型包含了流程的完整结构,包括所有节点(用户任务、网关、事件等)、流程线、表达式等信息。

Parameters:

  • processDefinitionId - 流程定义的唯一标识符,不能为null

Returns:

  • BpmnModel - BPMN 2.0模型对象,包含流程的完整结构

Throws:

  • FlowableObjectNotFoundException - 当指定ID的流程定义不存在时

常见使用场景

1. 流程图渲染

在流程监控页面,需要获取BPMN模型来渲染流程图,并高亮显示当前执行的节点。

2. 流程结构分析

分析流程定义的复杂度,统计用户任务数量、网关数量、子流程等信息。

3. 动态任务查询

获取流程中所有用户任务节点的配置信息,如任务名称、候选人、候选组等。

4. 流程验证

在流程部署前,解析BPMN模型,验证流程设计是否符合企业规范。

Kotlin + Spring Boot 调用示例

示例1:获取流程中的所有用户任务

kotlin
import org.flowable.bpmn.model.BpmnModel
import org.flowable.bpmn.model.UserTask
import org.flowable.engine.RepositoryService
import org.springframework.stereotype.Service

data class UserTaskInfo(
    val id: String,
    val name: String,
    val assignee: String?,
    val candidateUsers: List<String>,
    val candidateGroups: List<String>
)

@Service
class ProcessTaskAnalysisService(
    private val repositoryService: RepositoryService
) {
    
    /**
     * 获取流程中的所有用户任务
     * 企业场景:流程设计审查,检查所有需要人工处理的环节
     */
    fun getAllUserTasks(processDefinitionId: String): List<UserTaskInfo> {
        val bpmnModel = repositoryService.getBpmnModel(processDefinitionId)
        
        val mainProcess = bpmnModel.mainProcess
        
        val userTasks = mainProcess.findFlowElementsOfType(UserTask::class.java)
        
        return userTasks.map { task ->
            UserTaskInfo(
                id = task.id,
                name = task.name,
                assignee = task.assignee,
                candidateUsers = task.candidateUsers,
                candidateGroups = task.candidateGroups
            )
        }
    }
    
    /**
     * 打印流程任务结构
     */
    fun printProcessStructure(processDefinitionId: String) {
        val tasks = getAllUserTasks(processDefinitionId)
        
        println("=== 流程任务结构 ===")
        tasks.forEachIndexed { index, task ->
            println("${index + 1}. ${task.name} (${task.id})")
            println("   处理人: ${task.assignee ?: "未指定"}")
            println("   候选用户: ${task.candidateUsers.joinToString(", ").ifEmpty { "无" }}")
            println("   候选组: ${task.candidateGroups.joinToString(", ").ifEmpty { "无" }}")
        }
    }
}

示例2:生成流程图(带高亮节点)

kotlin
import org.flowable.bpmn.model.BpmnModel
import org.flowable.engine.RepositoryService
import org.flowable.engine.RuntimeService
import org.flowable.image.ProcessDiagramGenerator
import org.springframework.stereotype.Service
import java.io.InputStream

@Service
class ProcessDiagramService(
    private val repositoryService: RepositoryService,
    private val runtimeService: RuntimeService,
    private val processDiagramGenerator: ProcessDiagramGenerator
) {
    
    /**
     * 生成流程实例的执行图(高亮当前节点)
     * 企业场景:流程监控页面,展示当前流程执行到哪个环节
     */
    fun generateProcessInstanceDiagram(processInstanceId: String): InputStream {
        // 获取流程实例
        val processInstance = runtimeService.createProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .singleResult()
            ?: throw IllegalArgumentException("流程实例不存在")
        
        // 获取BPMN模型
        val bpmnModel = repositoryService.getBpmnModel(processInstance.processDefinitionId)
        
        // 获取当前活动节点
        val activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId)
        
        // 获取已完成的流程线
        val historicActivityInstances = runtimeService.createProcessInstanceQuery()
            .processInstanceId(processInstanceId)
            .singleResult()
        
        // 生成流程图
        return processDiagramGenerator.generateDiagram(
            bpmnModel,
            "png",
            activeActivityIds.toList(), // 高亮当前节点
            emptyList(), // 高亮的流程线
            "宋体", "宋体", "宋体", // 中文字体
            null,
            1.0,
            true
        )
    }
}

示例3:流程复杂度分析

kotlin
import org.flowable.bpmn.model.*
import org.flowable.engine.RepositoryService
import org.springframework.stereotype.Service

data class ProcessComplexity(
    val processName: String,
    val totalElements: Int,
    val userTaskCount: Int,
    val serviceTaskCount: Int,
    val gatewayCount: Int,
    val subProcessCount: Int,
    val eventCount: Int,
    val complexityLevel: String
)

@Service
class ProcessComplexityAnalyzer(
    private val repositoryService: RepositoryService
) {
    
    /**
     * 分析流程复杂度
     * 企业场景:流程设计规范检查,避免流程过于复杂难以维护
     */
    fun analyzeProcessComplexity(processDefinitionId: String): ProcessComplexity {
        val processDefinition = repositoryService.getProcessDefinition(processDefinitionId)
        val bpmnModel = repositoryService.getBpmnModel(processDefinitionId)
        
        val mainProcess = bpmnModel.mainProcess
        val allElements = mainProcess.flowElements
        
        // 统计各类元素数量
        val userTaskCount = mainProcess.findFlowElementsOfType(UserTask::class.java).size
        val serviceTaskCount = mainProcess.findFlowElementsOfType(ServiceTask::class.java).size
        val gatewayCount = mainProcess.findFlowElementsOfType(Gateway::class.java).size
        val subProcessCount = mainProcess.findFlowElementsOfType(SubProcess::class.java).size
        val eventCount = mainProcess.findFlowElementsOfType(Event::class.java).size
        
        // 计算复杂度等级
        val totalTasks = userTaskCount + serviceTaskCount
        val complexityLevel = when {
            totalTasks <= 5 && gatewayCount <= 2 -> "简单"
            totalTasks <= 10 && gatewayCount <= 5 -> "中等"
            totalTasks <= 20 && gatewayCount <= 10 -> "复杂"
            else -> "非常复杂"
        }
        
        return ProcessComplexity(
            processName = processDefinition.name,
            totalElements = allElements.size,
            userTaskCount = userTaskCount,
            serviceTaskCount = serviceTaskCount,
            gatewayCount = gatewayCount,
            subProcessCount = subProcessCount,
            eventCount = eventCount,
            complexityLevel = complexityLevel
        )
    }
    
    /**
     * 打印流程复杂度报告
     */
    fun printComplexityReport(processDefinitionId: String) {
        val complexity = analyzeProcessComplexity(processDefinitionId)
        
        println("""
            === 流程复杂度分析报告 ===
            流程名称: ${complexity.processName}
            复杂度等级: ${complexity.complexityLevel}
            
            元素统计:
            - 总元素数: ${complexity.totalElements}
            - 用户任务: ${complexity.userTaskCount}
            - 服务任务: ${complexity.serviceTaskCount}
            - 网关: ${complexity.gatewayCount}
            - 子流程: ${complexity.subProcessCount}
            - 事件: ${complexity.eventCount}
            
            建议:
            ${getComplexityAdvice(complexity.complexityLevel)}
        """.trimIndent())
    }
    
    private fun getComplexityAdvice(level: String): String {
        return when (level) {
            "简单" -> "流程结构清晰,易于理解和维护"
            "中等" -> "流程复杂度适中,建议添加详细的文档说明"
            "复杂" -> "流程较为复杂,建议考虑拆分为多个子流程"
            "非常复杂" -> "流程过于复杂,强烈建议重构,拆分为多个独立流程"
            else -> ""
        }
    }
}

示例4:提取流程变量定义

kotlin
import org.flowable.bpmn.model.BpmnModel
import org.flowable.bpmn.model.FlowElement
import org.flowable.bpmn.model.UserTask
import org.flowable.engine.RepositoryService
import org.springframework.stereotype.Service

data class ProcessVariableInfo(
    val variableName: String,
    val usedInTasks: List<String>,
    val usageType: String // "input", "output", "condition"
)

@Service
class ProcessVariableAnalyzer(
    private val repositoryService: RepositoryService
) {
    
    /**
     * 分析流程中使用的变量
     * 企业场景:流程测试准备,了解需要准备哪些测试数据
     */
    fun analyzeProcessVariables(processDefinitionId: String): List<ProcessVariableInfo> {
        val bpmnModel = repositoryService.getBpmnModel(processDefinitionId)
        val mainProcess = bpmnModel.mainProcess
        
        val variableUsage = mutableMapOf<String, MutableSet<String>>()
        
        // 分析用户任务中的表单字段
        mainProcess.findFlowElementsOfType(UserTask::class.java).forEach { task ->
            task.formProperties.forEach { formProperty ->
                variableUsage.getOrPut(formProperty.id) { mutableSetOf() }
                    .add("${task.name}(表单字段)")
            }
        }
        
        // TODO: 这里可以继续分析网关条件表达式、监听器等中使用的变量
        
        return variableUsage.map { (varName, tasks) ->
            ProcessVariableInfo(
                variableName = varName,
                usedInTasks = tasks.toList(),
                usageType = "表单变量"
            )
        }
    }
}

示例5:流程节点权限配置查询

kotlin
import org.flowable.bpmn.model.BpmnModel
import org.flowable.bpmn.model.UserTask
import org.flowable.engine.RepositoryService
import org.springframework.stereotype.Service

data class TaskPermissionConfig(
    val taskId: String,
    val taskName: String,
    val assigneeExpression: String?,
    val candidateUsers: List<String>,
    val candidateGroups: List<String>,
    val priority: Int?
)

@Service
class TaskPermissionService(
    private val repositoryService: RepositoryService
) {
    
    /**
     * 获取流程中所有任务的权限配置
     * 企业场景:权限审计,检查流程中的任务分配是否合规
     */
    fun getTaskPermissions(processDefinitionId: String): List<TaskPermissionConfig> {
        val bpmnModel = repositoryService.getBpmnModel(processDefinitionId)
        val mainProcess = bpmnModel.mainProcess
        
        val userTasks = mainProcess.findFlowElementsOfType(UserTask::class.java)
        
        return userTasks.map { task ->
            TaskPermissionConfig(
                taskId = task.id,
                taskName = task.name,
                assigneeExpression = task.assignee,
                candidateUsers = task.candidateUsers,
                candidateGroups = task.candidateGroups,
                priority = task.priority?.toIntOrNull()
            )
        }
    }
    
    /**
     * 检查任务是否有权限配置
     */
    fun validateTaskPermissions(processDefinitionId: String): Map<String, String> {
        val permissions = getTaskPermissions(processDefinitionId)
        val issues = mutableMapOf<String, String>()
        
        permissions.forEach { config ->
            val hasAssignee = !config.assigneeExpression.isNullOrBlank()
            val hasCandidates = config.candidateUsers.isNotEmpty() || 
                               config.candidateGroups.isNotEmpty()
            
            if (!hasAssignee && !hasCandidates) {
                issues[config.taskId] = "任务 '${config.taskName}' 未配置处理人或候选人"
            }
        }
        
        return issues
    }
}

示例6:流程文档生成器

kotlin
import org.flowable.bpmn.model.*
import org.flowable.engine.RepositoryService
import org.springframework.stereotype.Service

@Service
class ProcessDocumentationGenerator(
    private val repositoryService: RepositoryService
) {
    
    /**
     * 生成流程文档(Markdown格式)
     * 企业场景:自动生成流程说明文档,便于培训和知识传承
     */
    fun generateProcessDocumentation(processDefinitionId: String): String {
        val processDefinition = repositoryService.getProcessDefinition(processDefinitionId)
        val bpmnModel = repositoryService.getBpmnModel(processDefinitionId)
        val mainProcess = bpmnModel.mainProcess
        
        val doc = StringBuilder()
        
        // 标题
        doc.appendLine("# ${processDefinition.name}")
        doc.appendLine()
        doc.appendLine("**流程Key**: ${processDefinition.key}")
        doc.appendLine("**版本**: V${processDefinition.version}")
        doc.appendLine("**类别**: ${processDefinition.category ?: "未分类"}")
        doc.appendLine()
        
        // 流程说明
        doc.appendLine("## 流程说明")
        doc.appendLine(mainProcess.documentation ?: "无")
        doc.appendLine()
        
        // 用户任务列表
        doc.appendLine("## 任务节点")
        val userTasks = mainProcess.findFlowElementsOfType(UserTask::class.java)
        userTasks.forEachIndexed { index, task ->
            doc.appendLine("### ${index + 1}. ${task.name}")
            doc.appendLine("- **节点ID**: ${task.id}")
            doc.appendLine("- **处理人**: ${task.assignee ?: "动态分配"}")
            if (task.candidateGroups.isNotEmpty()) {
                doc.appendLine("- **候选组**: ${task.candidateGroups.joinToString(", ")}")
            }
            if (task.documentation != null) {
                doc.appendLine("- **说明**: ${task.documentation}")
            }
            doc.appendLine()
        }
        
        // 网关列表
        val gateways = mainProcess.findFlowElementsOfType(Gateway::class.java)
        if (gateways.isNotEmpty()) {
            doc.appendLine("## 分支节点")
            gateways.forEach { gateway ->
                doc.appendLine("- ${gateway.name ?: gateway.id} (${gateway.javaClass.simpleName})")
            }
            doc.appendLine()
        }
        
        return doc.toString()
    }
    
    /**
     * 保存流程文档到文件
     */
    fun saveProcessDocumentation(processDefinitionId: String, outputPath: String) {
        val documentation = generateProcessDocumentation(processDefinitionId)
        java.io.File(outputPath).writeText(documentation)
        println("流程文档已生成: $outputPath")
    }
}

示例7:REST API示例

kotlin
import org.flowable.bpmn.model.BpmnModel
import org.flowable.engine.RepositoryService
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping("/api/processes")
class ProcessModelController(
    private val repositoryService: RepositoryService
) {
    
    /**
     * 获取流程结构信息
     */
    @GetMapping("/{processDefinitionId}/structure")
    fun getProcessStructure(
        @PathVariable processDefinitionId: String
    ): Map<String, Any> {
        
        val bpmnModel = repositoryService.getBpmnModel(processDefinitionId)
        val mainProcess = bpmnModel.mainProcess
        
        val userTasks = mainProcess.findFlowElementsOfType(
            org.flowable.bpmn.model.UserTask::class.java
        )
        val gateways = mainProcess.findFlowElementsOfType(
            org.flowable.bpmn.model.Gateway::class.java
        )
        
        return mapOf(
            "processId" to mainProcess.id,
            "processName" to mainProcess.name,
            "userTaskCount" to userTasks.size,
            "gatewayCount" to gateways.size,
            "userTasks" to userTasks.map { mapOf(
                "id" to it.id,
                "name" to it.name,
                "assignee" to it.assignee
            )},
            "gateways" to gateways.map { mapOf(
                "id" to it.id,
                "name" to it.name,
                "type" to it.javaClass.simpleName
            )}
        )
    }
}

注意事项

1. BPMN模型解析

  • BpmnModel对象包含完整的流程结构,但不包含部署信息
  • 主流程通过 bpmnModel.mainProcess 获取
  • 子流程需要单独遍历处理

2. 中文编码问题

  • 生成流程图时,必须指定中文字体(如"宋体"、"微软雅黑")
  • 否则中文会显示为乱码或方框

3. 性能考虑

  • getBpmnModel会从数据库加载完整模型,建议缓存结果
  • 避免在循环中重复调用,一次获取后复用

4. 元素类型

  • 使用 findFlowElementsOfType() 查找特定类型的元素
  • 常用类型:UserTask, ServiceTask, Gateway, StartEvent, EndEvent, SubProcess等

5. 流程图生成

  • 需要依赖 flowable-image-generator 组件
  • 支持PNG、SVG等格式

相关 API

  • RepositoryService.getBpmnModel() - 获取BPMN模型
  • RepositoryService.getProcessDiagram() - 获取流程图输入流
  • RepositoryService.getProcessDiagramLayout() - 获取流程图布局信息
  • RepositoryService.validateProcess() - 验证BPMN模型
  • ProcessDiagramGenerator.generateDiagram() - 生成流程图

最佳实践

1. 缓存BPMN模型

kotlin
@Service
class CachedBpmnModelService(
    private val repositoryService: RepositoryService
) {
    
    private val cache = ConcurrentHashMap<String, BpmnModel>()
    
    fun getBpmnModel(processDefinitionId: String): BpmnModel {
        return cache.getOrPut(processDefinitionId) {
            repositoryService.getBpmnModel(processDefinitionId)
        }
    }
}

2. 封装通用解析方法

kotlin
fun BpmnModel.getAllUserTasks(): List<UserTask> {
    return this.mainProcess.findFlowElementsOfType(UserTask::class.java)
}

fun BpmnModel.getAllGateways(): List<Gateway> {
    return this.mainProcess.findFlowElementsOfType(Gateway::class.java)
}

本文档说明

  • 基于 Flowable 7.1.0 版本编写
  • 所有示例均可直接在 Spring Boot + Kotlin 项目中使用
  • 示例场景来自真实企业应用,具有实际参考价值