Skip to content

createHistoricTaskInstanceQuery

Flowable 7.1.0 摘要:创建历史任务实例查询对象,用于查询已完成或删除的任务历史数据。

方法签名与说明

HistoricTaskInstanceQuery createHistoricTaskInstanceQuery()

创建历史任务实例查询构建器。可以查询所有任务的历史记录,包括已完成、已删除的任务,以及当前正在执行的任务。

Returns:

  • HistoricTaskInstanceQuery - 历史任务实例查询构建器

常见使用场景

1. 用户任务历史记录

查询用户处理过的所有任务,用于工作量统计和绩效考核。

2. 任务执行效率分析

分析任务的平均处理时长,找出效率瓶颈。

3. 审批链路追溯

追溯某个业务单据的完整审批过程,查看每个环节的处理人和处理时间。

4. 超时任务监控

查找处理超时的任务,进行预警和催办。

Kotlin + Spring Boot 调用示例

示例1:查询用户处理过的任务

kotlin
import org.flowable.engine.HistoryService
import org.springframework.stereotype.Service
import java.util.Date

data class UserTaskHistory(
    val taskId: String,
    val taskName: String,
    val processName: String,
    val startTime: Date,
    val endTime: Date?,
    val duration: Long?,
    val assignee: String?
)

@Service
class UserTaskHistoryService(
    private val historyService: HistoryService
) {
    
    /**
     * 查询用户处理过的所有任务
     * 企业场景:员工个人工作台,查看历史任务记录
     */
    fun getUserTaskHistory(userId: String): List<UserTaskHistory> {
        val historicTasks = historyService.createHistoricTaskInstanceQuery()
            .taskAssignee(userId)
            .orderByHistoricTaskInstanceEndTime()
            .desc()
            .list()
        
        return historicTasks.map { task ->
            UserTaskHistory(
                taskId = task.id,
                taskName = task.name,
                processName = task.processDefinitionName ?: "未知流程",
                startTime = task.startTime,
                endTime = task.endTime,
                duration = task.durationInMillis,
                assignee = task.assignee
            )
        }
    }
}

示例2:任务执行效率统计

kotlin
import org.flowable.engine.HistoryService
import org.springframework.stereotype.Service
import java.util.Date

data class TaskEfficiencyStats(
    val taskDefinitionKey: String,
    val taskName: String,
    val totalCount: Long,
    val avgDurationHours: Double,
    val maxDurationHours: Double,
    val minDurationHours: Double
)

@Service
class TaskEfficiencyAnalyzer(
    private val historyService: HistoryService
) {
    
    /**
     * 统计任务执行效率
     * 企业场景:流程优化,分析各环节的平均处理时长
     */
    fun analyzeTaskEfficiency(
        processDefinitionKey: String,
        startDate: Date,
        endDate: Date
    ): List<TaskEfficiencyStats> {
        
        // 查询该流程在时间范围内的所有已完成任务
        val historicTasks = historyService.createHistoricTaskInstanceQuery()
            .processDefinitionKey(processDefinitionKey)
            .taskCompletedAfter(startDate)
            .taskCompletedBefore(endDate)
            .finished()
            .list()
        
        // 按任务定义分组统计
        val taskGroups = historicTasks.groupBy { it.taskDefinitionKey }
        
        return taskGroups.map { (taskKey, tasks) ->
            val durations = tasks.mapNotNull { it.durationInMillis }
                .map { it.toDouble() / (1000 * 60 * 60) } // 转换为小时
            
            TaskEfficiencyStats(
                taskDefinitionKey = taskKey,
                taskName = tasks.first().name,
                totalCount = tasks.size.toLong(),
                avgDurationHours = if (durations.isNotEmpty()) durations.average() else 0.0,
                maxDurationHours = durations.maxOrNull() ?: 0.0,
                minDurationHours = durations.minOrNull() ?: 0.0
            )
        }
    }
}

示例3:查询请假审批链路

kotlin
import org.flowable.engine.HistoryService
import org.springframework.stereotype.Service

data class ApprovalStep(
    val stepName: String,
    val assignee: String,
    val startTime: String,
    val endTime: String?,
    val duration: String,
    val comment: String?
)

@Service
class ApprovalChainService(
    private val historyService: HistoryService
) {
    
    /**
     * 查询流程的审批链路
     * 企业场景:请假申请详情页,展示审批过程
     */
    fun getApprovalChain(processInstanceId: String): List<ApprovalStep> {
        val historicTasks = historyService.createHistoricTaskInstanceQuery()
            .processInstanceId(processInstanceId)
            .orderByHistoricTaskInstanceStartTime()
            .asc()
            .list()
        
        return historicTasks.map { task ->
            val duration = task.durationInMillis?.let {
                val hours = it / (1000 * 60 * 60)
                val minutes = (it % (1000 * 60 * 60)) / (1000 * 60)
                "${hours}小时${minutes}分钟"
            } ?: "进行中"
            
            ApprovalStep(
                stepName = task.name,
                assignee = task.assignee ?: "待分配",
                startTime = task.startTime.toString(),
                endTime = task.endTime?.toString(),
                duration = duration,
                comment = null // 可以通过CommentService获取
            )
        }
    }
}

示例4:超时任务监控

kotlin
import org.flowable.engine.HistoryService
import org.springframework.stereotype.Service
import java.util.Calendar
import java.util.Date

data class TimeoutTask(
    val taskId: String,
    val taskName: String,
    val processInstanceId: String,
    val assignee: String?,
    val startTime: Date,
    val expectedDuration: Long, // 预期处理时长(小时)
    val actualDuration: Long, // 实际已用时长(小时)
    val isOverdue: Boolean
)

@Service
class TaskTimeoutMonitor(
    private val historyService: HistoryService
) {
    
    /**
     * 监控超时任务
     * 企业场景:任务预警系统,发现超时未处理的任务
     */
    fun monitorTimeoutTasks(
        processDefinitionKey: String,
        expectedHours: Long = 24
    ): List<TimeoutTask> {
        
        val timeoutDate = Calendar.getInstance().apply {
            add(Calendar.HOUR, -expectedHours.toInt())
        }.time
        
        // 查询未完成且超过预期时长的任务
        val unfinishedTasks = historyService.createHistoricTaskInstanceQuery()
            .processDefinitionKey(processDefinitionKey)
            .unfinished()
            .taskCreatedBefore(timeoutDate)
            .list()
        
        val now = System.currentTimeMillis()
        
        return unfinishedTasks.map { task ->
            val actualHours = (now - task.startTime.time) / (1000 * 60 * 60)
            
            TimeoutTask(
                taskId = task.id,
                taskName = task.name,
                processInstanceId = task.processInstanceId,
                assignee = task.assignee,
                startTime = task.startTime,
                expectedDuration = expectedHours,
                actualDuration = actualHours,
                isOverdue = actualHours > expectedHours
            )
        }
    }
}

示例5:用户工作量统计

kotlin
import org.flowable.engine.HistoryService
import org.springframework.stereotype.Service
import java.util.Date
import java.util.Calendar

data class UserWorkload(
    val userId: String,
    val totalTasks: Long,
    val completedTasks: Long,
    val pendingTasks: Long,
    val avgProcessingHours: Double,
    val completionRate: Double
)

@Service
class UserWorkloadService(
    private val historyService: HistoryService
) {
    
    /**
     * 统计用户工作量
     * 企业场景:月度绩效考核,统计员工任务完成情况
     */
    fun getUserWorkload(userId: String, month: Int, year: Int): UserWorkload {
        val startDate = Calendar.getInstance().apply {
            set(year, month - 1, 1, 0, 0, 0)
        }.time
        
        val endDate = Calendar.getInstance().apply {
            set(year, month, 1, 0, 0, 0)
        }.time
        
        // 查询该用户在该月的所有任务
        val allTasks = historyService.createHistoricTaskInstanceQuery()
            .taskAssignee(userId)
            .taskCreatedAfter(startDate)
            .taskCreatedBefore(endDate)
            .list()
        
        val completedTasks = allTasks.filter { it.endTime != null }
        val pendingTasks = allTasks.filter { it.endTime == null }
        
        val avgHours = if (completedTasks.isNotEmpty()) {
            val totalDuration = completedTasks.mapNotNull { it.durationInMillis }.sum()
            (totalDuration.toDouble() / completedTasks.size) / (1000 * 60 * 60)
        } else 0.0
        
        val completionRate = if (allTasks.isNotEmpty()) {
            (completedTasks.size.toDouble() / allTasks.size) * 100
        } else 0.0
        
        return UserWorkload(
            userId = userId,
            totalTasks = allTasks.size.toLong(),
            completedTasks = completedTasks.size.toLong(),
            pendingTasks = pendingTasks.size.toLong(),
            avgProcessingHours = avgHours,
            completionRate = completionRate
        )
    }
}

示例6:任务历史REST API

kotlin
import org.flowable.engine.HistoryService
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.web.bind.annotation.*
import java.util.Date

@RestController
@RequestMapping("/api/history/tasks")
class HistoricTaskController(
    private val historyService: HistoryService
) {
    
    /**
     * 查询任务历史
     */
    @GetMapping
    fun listHistoricTasks(
        @RequestParam(required = false) assignee: String?,
        @RequestParam(required = false) processInstanceId: String?,
        @RequestParam(required = false) taskName: String?,
        @RequestParam(required = false) finished: Boolean?,
        @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") startDate: Date?,
        @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") endDate: Date?,
        @RequestParam(defaultValue = "1") page: Int,
        @RequestParam(defaultValue = "10") size: Int
    ): Map<String, Any> {
        
        val query = historyService.createHistoricTaskInstanceQuery()
        
        assignee?.let { query.taskAssignee(it) }
        processInstanceId?.let { query.processInstanceId(it) }
        taskName?.let { query.taskNameLike("%$it%") }
        startDate?.let { query.taskCreatedAfter(it) }
        endDate?.let { query.taskCreatedBefore(it) }
        
        finished?.let {
            if (it) query.finished() else query.unfinished()
        }
        
        val total = query.count()
        val tasks = query
            .orderByHistoricTaskInstanceEndTime()
            .desc()
            .listPage((page - 1) * size, size)
        
        return mapOf(
            "data" to tasks.map { task ->
                mapOf(
                    "taskId" to task.id,
                    "taskName" to task.name,
                    "assignee" to task.assignee,
                    "startTime" to task.startTime,
                    "endTime" to task.endTime,
                    "duration" to task.durationInMillis,
                    "processInstanceId" to task.processInstanceId
                )
            },
            "total" to total,
            "page" to page,
            "size" to size
        )
    }
}

注意事项

1. 历史数据范围

  • 查询包含所有任务:已完成、未完成、已删除
  • 使用 finished() 只查询已完成的任务
  • 使用 unfinished() 只查询未完成的任务

2. 性能优化

  • 添加时间范围条件,避免全表扫描
  • 大数据量必须使用分页查询
  • 考虑对历史表建立索引

3. 任务时长计算

  • durationInMillis 只有已完成的任务才有值
  • 未完成的任务需要手动计算:当前时间 - 开始时间

4. 多租户隔离

  • 多租户场景必须添加 processInstanceTenantId() 条件

5. 删除的任务

  • 即使任务被删除,历史记录仍然存在
  • 可通过 deleteReason 判断任务删除原因

相关 API

  • HistoryService.createHistoricTaskInstanceQuery() - 创建查询
  • HistoryService.createHistoricProcessInstanceQuery() - 查询流程历史
  • HistoryService.createHistoricActivityInstanceQuery() - 查询活动历史
  • HistoryService.createHistoricVariableInstanceQuery() - 查询变量历史
  • HistoryService.deleteHistoricTaskInstance() - 删除任务历史

最佳实践

1. 定期归档历史任务

kotlin
@Scheduled(cron = "0 0 3 * * ?")
fun archiveHistoricTasks() {
    val sixMonthsAgo = Calendar.getInstance().apply {
        add(Calendar.MONTH, -6)
    }.time
    
    val oldTasks = historyService.createHistoricTaskInstanceQuery()
        .finished()
        .taskCompletedBefore(sixMonthsAgo)
        .list()
    
    // 归档逻辑...
}

2. 缓存统计结果

kotlin
@Cacheable("taskStats")
fun getCachedTaskStats(userId: String, month: Int, year: Int) {
    return getUserWorkload(userId, month, year)
}

本文档说明

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