Appearance
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 项目中使用
- 示例场景来自真实企业应用,具有实际参考价值