第一次用Flowable做项目时,我总觉得"待办列表"才是核心功能。直到某天业务部门投诉说:"审批过的合同找不到了,财务不认账!"才发现已办功能的价值远超想象。这就像网购只看订单却查不到物流记录,流程走到哪、谁处理的、结果如何——这些信息缺失会让整个业务流程变成黑箱。
已办数据的四大黄金价值你可能没意识到:
我们团队在金融项目实测发现:上线"高级已办查询"功能后,客服处理投诉的时效从45分钟缩短到12分钟。因为客服能直接看到客户历史业务的完整审批链,不用再跨系统查数据。
千万别在Controller里直接写HistoryService查询!我见过有开发者把20行查询逻辑全堆在接口里,后期加缓存和审计时差点重构到崩溃。正确的分层应该像洋葱:
java复制// 反面教材:所有逻辑堆在Controller
@PostMapping("/badExample")
public List<HistoricTaskInstance> badDemo(String userId) {
return historyService.createHistoricTaskInstanceQuery()
.taskAssignee(userId)
.finished()
.list();
}
// 推荐结构
@Service
public class TaskQueryService {
// 带缓存的分页查询
@Cacheable(key = "'completed:'+#user.id+':'+#pageReq.hashCode()")
public PageResult<CompletedTaskDTO> queryCompletedTasks(User user, PageReq pageReq) {
// 参数校验 -> 缓存检查 -> 数据查询 -> 格式转换
}
}
性能优化实测数据:
直接从HistoricTaskInstance取字段会踩坑!比如流程定义名称应该从ProcessDefinition获取,而不是存冗余字段。我整理了个安全映射指南:
| 原始字段 | 推荐来源 | 注意事项 |
|---|---|---|
| 流程名称 | RepositoryService查ProcessDefinition | 要处理流程版本变更情况 |
| 发起人 | HistoricProcessInstance.startUserId | 需转用户服务查询真实姓名 |
| 处理时长 | historicTaskInstance.endTime - startTime | 注意时区转换 |
java复制// 智能字段映射示例
vo.setProcessDuration(
ChronoUnit.MINUTES.between(
task.getStartTime().toInstant(),
task.getEndTime().toInstant()
) + "分钟"
);
别再用ElementUI的el-table直接展示了!我们给政务系统做的增强方案获得客户好评:
vue复制<template>
<div class="timeline-container">
<el-timeline>
<el-timeline-item
v-for="task in enhancedTasks"
:key="task.id"
:timestamp="formatTime(task.endTime)"
placement="top">
<el-card shadow="hover">
<template #header>
<div class="card-header">
<process-badge :type="task.processType"/>
<time-diff :start="task.startTime" :end="task.endTime"/>
</div>
</template>
<comment-panel :task-id="task.id"/>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
</template>
增强功能包:
前端缓存不是简单的localStorage.setItem!要考虑:
completed_${userId}_page${page}_size${size}javascript复制// 智能缓存控制器
const cacheHandler = {
get(key) {
const encrypted = localStorage.getItem(key);
return encrypted ? JSON.parse(decrypt(encrypted)) : null;
},
set(key, data) {
const encrypted = encrypt(JSON.stringify(data));
localStorage.setItem(key, encrypted);
// 广播缓存更新事件
eventBus.emit('cache-update', { key, data });
}
}
有次凌晨2点被紧急电话叫醒:某员工能看到CEO审批过的合同。这就是权限过滤不到位的典型事故。必须三重校验:
taskAssignee(currentUser)java复制// 安全查询示例
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
.taskAssignee(currentUser)
.processVariableValueEquals("department", userDept);
if (!isAdmin()) {
query.variableValueEquals("confidentialLevel", "public");
}
审计日志必备字段:
记得在AOP切面里加上@AuditLog(actionType = "QUERY_COMPLETED_TASKS"),我们靠这个功能在一次数据泄露事件中快速定位到了误操作人员。