Appearance
claim
在 Flowable 中,claim 方法用于用户认领任务。当任务分配给候选用户组时,组内的用户可以使用此方法认领任务,成为任务的分配人。
语法示例
kotlin
/**
* 认领任务(Kotlin 视角)
* Flowable Java API 为 void;Kotlin 中等价为 Unit。
* @param taskId 要认领的任务ID
* @param userId 认领任务的用户ID
*/
// Kotlin 中直接调用:返回类型等价于 Unit
taskService.claim(taskId, userId)
// 如需签名说明(非真实扩展),可理解为:
fun TaskService.claim(taskId: String, userId: String): Unit使用场景
- 候选人任务认领:候选用户组中的用户认领任务
- 工作负载分配:团队成员主动认领适合的任务
- 任务分派:管理员为用户分派任务
- 工作流协作:多人协作场景中的任务分配
真实案例
呼叫中心督办任务通常由“客服质检组”成员自行挑选处理。前端点击“认领”按钮后,需要校验该客服是否具备候选资格,并调用 Flowable 完成认领动作。
kotlin
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api/support/tasks")
class SupportTaskController(
private val supportTaskClaimService: SupportTaskClaimService
) {
/**
* 客服在质检工作台点击“我要处理”时触发
* - taskId 来自路径变量
* - userId 通过请求头传入(示例中使用 X-Operator-Id 表示当前操作用户)
* 成功时返回 204 No Content,前端据此刷新页面或提示成功
*/
@PostMapping("/{taskId}/claim")
fun claim(
@PathVariable taskId: String, // 任务 ID
@RequestHeader("X-Operator-Id") userId: String // 当前操作用户 ID
): ResponseEntity<Void> {
// 委托给领域服务进行业务校验与 Flowable 调用
supportTaskClaimService.claimTask(taskId, userId)
// 无响应体,仅表示操作成功
return ResponseEntity.noContent().build()
}
}kotlin
import org.flowable.engine.TaskService
import org.flowable.identitylink.api.IdentityLinkType
import org.flowable.task.api.Task
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.http.HttpStatus
import org.springframework.web.server.ResponseStatusException
@Service
class SupportTaskClaimService(
private val taskService: TaskService
) {
@Transactional
fun claimTask(taskId: String, userId: String) {
// 1) 查询任务是否存在
val task: Task = taskService.createTaskQuery()
.taskId(taskId)
.singleResult()
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "质检任务不存在")
// 2) 幂等:若已被当前用户认领,当作成功返回
if (task.assignee == userId) return
// 3) 若已被其他人认领,返回 409 Conflict
if (task.assignee != null && task.assignee != userId) {
throw ResponseStatusException(HttpStatus.CONFLICT, "任务已被 ${task.assignee} 认领")
}
// 4) 取出任务的身份链接,身份链接包含候选用户/候选组等信息
val candidateLinks = taskService.getIdentityLinksForTask(taskId)
// 判断当前用户是否直接是候选用户
val userIsCandidate = candidateLinks.any { link ->
link.type == IdentityLinkType.CANDIDATE && link.userId == userId
}
// 判断当前用户是否属于某个候选用户组
val groupIsCandidate = candidateLinks.any { link ->
link.type == IdentityLinkType.CANDIDATE && link.groupId != null &&
userBelongsToGroup(userId, link.groupId)
}
// 若既不是候选用户,也不在候选组,则无权认领
if (!userIsCandidate && !groupIsCandidate) {
throw ResponseStatusException(HttpStatus.FORBIDDEN, "当前用户无权认领该任务")
}
// 5) 满足条件后,调用 Flowable 的认领 API;在高并发下可能抛出“已被认领”的特定异常
try {
taskService.claim(taskId, userId)
} catch (ex: RuntimeException) {
// 注意:具体异常类名随版本可能不同(例如 FlowableTaskAlreadyClaimedException)
val name = ex::class.simpleName ?: ""
if (name.contains("TaskAlreadyClaimed", ignoreCase = true)) {
throw ResponseStatusException(HttpStatus.CONFLICT, "任务已被他人认领")
}
throw ex
}
// 6) 认领成功后,可进行审计记录、埋点、异步通知等
println("客服 $userId 认领质检任务 $taskId")
}
private fun userBelongsToGroup(userId: String, groupId: String): Boolean {
// 真实场景:可对接 IAM/组织架构服务,或使用 Flowable IdentityService 校验成员关系
// 示例中直接返回 true 表示放行
return true
}
}认领策略
1. 先到先得策略
kotlin
// 最简单的认领策略:谁先点“认领”谁获得任务
// 注意:在高并发环境中仍需依赖底层引擎的并发控制与事务,避免脏写
taskService.claim(taskId, userId)2. 能力匹配策略
kotlin
// 认领前先做能力/资质校验:用户是否具备处理该任务的必要资质
// 例如:证书、角色、业务域、优先级权限等
if (isUserQualified(userId, taskRequirements)) {
taskService.claim(taskId, userId)
}3. 负载均衡策略
kotlin
// 在候选集合中挑选当前负载(在办+待办数量、权重)最小的用户进行认领
// 可结合队列长度、SLA、峰值控制等维度综合计算负载
val lightestLoadUser = findUserWithLightestLoad(candidateUsers)
taskService.claim(taskId, lightestLoadUser)重要说明
- 候选人验证:只有任务的候选人才能认领任务
- 状态检查:只有未分配的任务才能被认领
- 并发安全:在高并发环境下可能出现竞争条件
- 权限控制:应实现适当的权限验证机制
- 负载管理:避免单个用户认领过多任务
- 事务建议:对认领操作加上事务管理(@Transactional),确保一致性
- 幂等设计:对“已被当前用户认领”的重复请求返回成功,提升用户体验
相关方法
unclaim(String): 释放已认领的任务setAssignee(String, String): 直接设置任务分配人addCandidateUser(String, String): 添加候选用户getIdentityLinksForTask(String): 获取任务的身份链接
最佳实践
- 权限验证:认领前验证用户是否为候选人
- 负载检查:避免用户过载,控制认领数量
- 技能匹配:实现基于技能的智能认领
- 并发处理:处理多用户同时认领的竞争情况
- 审计日志:记录认领操作用于审计和分析
- 错误码映射:将“任务不存在/无权限/已被认领”等映射到 404/403/409,便于前端处理
候选组校验的实现选项
示例中 userBelongsToGroup 使用了占位返回。实际工程中可以:
- 对接外部 IAM/组织架构系统,查询用户与组关系;或
- 使用 Flowable 的
IdentityService进行校验(若采用 Flowable 作为身份源):
kotlin
// 伪代码示例:根据实际项目注入 identityService
val isMember = identityService.createGroupQuery()
.groupId(groupId)
.groupMember(userId)
.count() > 0方法语义对比与使用建议
claim(taskId, userId):将任务分配给 userId;常用于候选任务被个人接单。不会自动移除候选身份链接。unclaim(taskId):释放已认领的任务,让其重新回到候选池;常用于撤销接单或转回候选。setAssignee(taskId, userId):直接设置办理人,通常用于管理员强制分派或系统策略路由;绕过“候选人自行认领”的流程。