查询指定任务的办理时间轴

一、接口核心需求分析

你提供的这段代码是Activiti工作流中查询指定任务的办理时间轴接口,核心业务需求如下:

  1. 关联查询:根据传入的任务ID(taskId),先获取对应的流程实例ID(processInstanceId);
  2. 历史轨迹查询:查询该流程实例下所有“用户任务(userTask)”的历史执行记录,按任务开始时间升序排列;
  3. 数据格式化:将历史任务记录转换为前端易展示的TaskInfo对象,包含任务名称、办理人、开始/结束时间、审批意见等核心信息;
  4. 结果返回:返回结构化的任务办理时间轴列表,支撑前端展示流程审批轨迹。

二、代码逐段解析(核心逻辑+设计思路)

1. 流程实例ID关联
StringprocessInstanceId=taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
  • 设计思路
    • 通过TaskServicetaskQuerytaskId查询具体任务,获取该任务所属的流程实例ID;
    • 这是“任务→流程实例→历史任务”关联的核心步骤,因为历史任务需按流程实例维度查询。
  • 风险点singleResult()可能返回null(如taskId不存在),直接调用getProcessInstanceId()会抛出空指针异常。
2. 历史用户任务查询
List<HistoricActivityInstance>history=historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)// 按流程实例过滤.activityType("userTask")// 仅查询用户任务(排除网关、开始/结束节点等).orderByHistoricActivityInstanceStartTime().asc()// 按开始时间升序(时间轴顺序).list();
  • 设计思路
    • 借助HistoryServiceHistoricActivityInstanceQuery查询流程实例的历史活动记录;
    • 核心过滤条件activityType("userTask"):只保留人工办理的任务,过滤自动节点(如网关、服务任务),符合“办理时间轴”的业务场景;
    • 排序规则:按任务开始时间升序,保证时间轴从流程发起→后续审批的顺序展示。
3. 历史数据转换为前端DTO
List<TaskInfo>infos=newArrayList<>();SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");history.stream().forEach(h->{TaskInfoinfo=newTaskInfo();info.setProcessInstanceId(h.getProcessInstanceId());info.setStartTime(sdf.format(h.getStartTime()));if(h.getEndTime()!=null){info.setEndTime(sdf.format(h.getEndTime()));}info.setAssignee(h.getAssignee());// 任务办理人info.setTaskName(h.getActivityName());// 任务名称(如“采购经理审批”)// 查询任务审批意见List<Comment>comments=taskService.getTaskComments(h.getTaskId());if(comments.size()>0){info.setComment(comments.get(0).getFullMessage());}infos.add(info);});
  • 设计思路
    • 数据适配:将Activiti原生的HistoricActivityInstance对象转换为自定义TaskInfo(前端DTO),屏蔽框架底层字段,只返回业务所需信息;
    • 时间格式化:将Date类型的开始/结束时间转换为“yyyy-MM-dd HH:mm:ss”字符串,便于前端展示;
    • 审批意见关联:通过taskService.getTaskComments(h.getTaskId())查询任务的审批备注(评论),取第一条作为核心意见。

三、核心问题与优化方案

1. 空指针异常(最高优先级优化)

原代码存在多处空指针风险,需补充校验:

@ApiOperation("任务办理时间轴")@RequestMapping(value="/history/{taskId}",method=RequestMethod.GET)@ResponseBodypublicList<TaskInfo>history(@PathVariableStringtaskId){// 1. 校验入参if(StringUtils.isBlank(taskId)){returnCollections.emptyList();// 或返回AjaxResult提示参数为空}// 2. 查询任务时校验nullTasktask=taskService.createTaskQuery().taskId(taskId).singleResult();if(task==null){returnCollections.emptyList();// 无对应任务,返回空列表}StringprocessInstanceId=task.getProcessInstanceId();// 3. 查询历史任务List<HistoricActivityInstance>history=historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).activityType("userTask").orderByHistoricActivityInstanceStartTime().asc().list();List<TaskInfo>infos=newArrayList<>();SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 4. 遍历历史任务时避免空指针for(HistoricActivityInstanceh:history){if(h==null){continue;}TaskInfoinfo=newTaskInfo();info.setProcessInstanceId(h.getProcessInstanceId());// 时间格式化校验if(h.getStartTime()!=null){info.setStartTime(sdf.format(h.getStartTime()));}if(h.getEndTime()!=null){info.setEndTime(sdf.format(h.getEndTime()));}info.setAssignee(h.getAssignee());info.setTaskName(h.getActivityName());// 审批意见查询(校验taskId非空)if(StringUtils.isNotBlank(h.getTaskId())){List<Comment>comments=taskService.getTaskComments(h.getTaskId());if(comments!=null&&!comments.isEmpty()){info.setComment(comments.get(0).getFullMessage());}}infos.add(info);}returninfos;}
2. 性能优化
  • 问题:遍历历史任务时,每次调用taskService.getTaskComments(h.getTaskId())都会发起一次数据库查询,若历史任务较多(如10+),会产生N+1查询问题;
  • 优化方案:批量查询所有任务的审批意见,减少数据库交互:
    // 1. 先收集所有历史任务IDList<String>taskIds=history.stream().filter(h->StringUtils.isNotBlank(h.getTaskId())).map(HistoricActivityInstance::getTaskId).collect(Collectors.toList());// 2. 批量查询评论(需自定义SQL/扩展API,Activiti原生无批量查询评论接口)// 示例:通过自定义Mapper批量查询Map<String,Comment>taskCommentMap=commentMapper.getCommentByTaskIds(taskIds);// 3. 遍历历史任务时直接从Map取值for(HistoricActivityInstanceh:history){// ... 其他赋值逻辑Commentcomment=taskCommentMap.get(h.getTaskId());if(comment!=null){info.setComment(comment.getFullMessage());}}
3. 时间格式化优化
  • 问题SimpleDateFormat是非线程安全的,若接口高并发调用,可能出现时间格式化错误;
  • 优化方案:使用DateTimeFormatter(Java 8+)替代:
    DateTimeFormatterformatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// 时间格式化if(h.getStartTime()!=null){info.setStartTime(h.getStartTime().toInstant().atZone(ZoneId.systemDefault()).format(formatter));}if(h.getEndTime()!=null){info.setEndTime(h.getEndTime().toInstant().atZone(ZoneId.systemDefault()).format(formatter));}
4. 异常处理

补充全局异常捕获,避免接口抛出未处理的异常:

@ApiOperation("任务办理时间轴")@RequestMapping(value="/history/{taskId}",method=RequestMethod.GET)@ResponseBodypublicAjaxResulthistory(@PathVariableStringtaskId){// 改为返回统一响应体try{// 核心逻辑(上述优化后的代码)List<TaskInfo>infos=...;returnAjaxResult.success(infos);}catch(Exceptione){log.error("查询任务办理时间轴失败,taskId:{}",taskId,e);returnAjaxResult.error("查询任务办理轨迹失败:"+e.getMessage());}}

四、设计维度温习

1. 架构设计
  • 分层设计:接口属于“应用层”,依赖Activiti的TaskService/HistoryService(领域层)完成数据查询,符合分层架构规范;
  • DTO设计:通过TaskInfo封装前端所需字段,实现“领域模型→展示模型”的解耦,避免直接返回框架原生对象。
2. 扩展性设计
  • 多维度过滤:可扩展支持按“办理人、任务状态、时间范围”过滤历史任务,例如:
    // 扩展:仅查询指定办理人的任务historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).activityType("userTask").assignee("zhangsan")// 扩展过滤条件.list();
  • 多语言适配:时间格式化可扩展支持国际化(如DateTimeFormatter结合Locale);
  • 审批意见扩展:原代码仅取第一条评论,可扩展返回所有评论,或按评论类型过滤(如“审批意见/备注”)。
3. 安全与规范
  • 权限控制:建议添加权限校验(如@PreAuthorize),仅允许流程参与人/管理员查询时间轴;
  • 日志规范:添加入参、出参日志,便于问题排查:
    log.info("查询任务办理时间轴,taskId:{}",taskId);log.info("查询结果:{}",infos.size());

五、核心总结

维度核心要点
需求核心按任务ID关联流程实例,查询所有用户任务的历史轨迹,格式化后返回办理时间、办理人、审批意见等信息
设计亮点聚焦“用户任务”过滤非业务节点,按时间升序保证时间轴顺序,适配前端展示格式
优化方向1. 补充空指针校验;2. 替换线程不安全的SimpleDateFormat;3. 批量查询评论避免N+1;4. 统一异常处理和日志;5. 添加权限控制
扩展场景支持多条件过滤、多语言时间格式化、批量返回审批意见

该接口是工作流审批轨迹展示的核心接口,优化后可解决空指针、性能、线程安全等问题,同时保持良好的扩展性,适配企业级应用的生产环境需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1125648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matlab 基于蚁群算法优化高斯过程回归(ACO-GPR)的数据多变量回归预测+交叉验证 (多输入单输出)

目录 1、代码简介 2、代码运行结果展示 3、代码获取 1、代码简介 Matlab 基于蚁群算法优化高斯过程回归(ACO-GPR)的数据多变量回归预测交叉验证 (多输入单输出) 默认5折&#xff0c;折数可调&#xff01;采用交叉验证一定程度上抑制了过拟合的发生&#xff01; 1.程序已经…

通过`exclude`排除冲突的Security自动配置类,解决Activiti与若依权限体系的兼容性问题;

你提供的这段代码是若依&#xff08;RuoYi&#xff09;框架集成Activiti后的启动类核心配置&#xff0c;我会先解析这段配置的核心作用&#xff0c;再说明关键细节和扩展优化方向。 一、代码核心需求与作用解析 你编写的RuoYiApplication启动类通过SpringBootApplication的excl…

MYSQL 创建索引

目录 自动索引 手动创建 主键索引 唯一索引 普通索引 创建复合索引 查看索引 删除主键 自动索引 如果我们为一张表添加主键约束、外键约束、唯一约束时&#xff0c;MYSQL会为对应的列自动创建一个索引。如果不指定任何约束时&#xff0c;MYSQL会自动为每一列生成一个索…

Thinkphp和Laravel框架的流浪猫领养系统_ct8whxh8

目录 ThinkPHP与Laravel框架的流浪猫领养系统对比分析系统架构与开发效率功能模块设计数据库与性能优化安全性与社区支持部署与维护 项目开发技术介绍PHP核心代码部分展示系统结论源码获取/同行可拿货,招校园代理 ThinkPHP与Laravel框架的流浪猫领养系统对比分析 系统架构与开…

MySql 8.0安装教程(windows系统),全网最详细的教程

目录 前言安装步骤安装包下载 前言 MySQL数据库服务是一个完全托管的数据库服务&#xff0c;是目前比较流行的开源数据库来部署云原生应用程序&#xff0c;不管在企业开发&#xff0c;个人开发&#xff0c;学校做项目都是必备的一款工具&#xff0c;并且MySQL是开源的&#…

Tailwind CSS:告别传统CSS,拥抱原子化开发新时代

想象一下&#xff0c;你在开发一个项目时&#xff0c;不再需要为CSS命名而烦恼&#xff0c;不再需要频繁在HTML和CSS文件间切换&#xff0c;更不必担心样式冲突和重复代码——这一切都源于一个名为Tailwind CSS的工具类优先框架。 Tailwind CSS的核心理念是“功能类优先”&…

RK3576基于UVC协议USB摄像头配置指南,手把手教你搞定

瑞芯微RK3576芯片平台基于UVC协议&#xff0c;配置USB摄像头实现即插即用的适配采集功能。演示设备&#xff1a;触觉智能RK3576开发板Purple Pi OH2系统固件为&#xff1a;Buildroot2024其配套RK3576核心板&#xff0c;尺寸40.540.5mm&#xff0c;邮票孔封装&#xff0c;支持LP…

性能指标笔记

网络指标 TTFB Time to First Byte 衡量请求资源到响应第一个字节开始到达之间的时间&#xff0c;结合 网络 面吧的资源请求耗时&#xff0c;可以判断主要延迟在哪一层。 关于 Network 网络面板&#xff1a; Queued at&#xff1a;表示请求何时入队&#xff0c;如果是 HTTP/1…

Thinkphp和Laravel框架的海关出入口货物报关统筹管理系统_szdhjj06

目录摘要概述技术架构对比核心功能模块性能与安全优化实施效果与扩展性项目开发技术介绍PHP核心代码部分展示系统结论源码获取/同行可拿货,招校园代理摘要概述 ThinkPHP和Laravel框架在海关出入口货物报关统筹管理系统中的应用&#xff0c;展现了现代PHP框架在复杂业务场景下的…

ubuntu系统_每天定时23:00 定时关机_怎么实现

在Ubuntu系统中设置每天23:00定时关机&#xff0c;最可靠的方法是使用系统内置的 cron 服务。下面的表格汇总了实现步骤和关键要点&#xff0c;你可以快速了解整个过程。步骤核心操作说明/命令示例1. 编辑计划任务终端中输入 sudo crontab -e使用 sudo 获取root权限&#xff0c…

HTML 表单元素与 React 中的其他 DOM 元素有所不同

React 表单与事件 本章节我们将讨论如何在 React 中使用表单。 HTML 表单元素与 React 中的其他 DOM 元素有所不同,因为表单元素生来就保留一些内部状态。 在 HTML 当中&#xff0c;像 <input>, <textarea>, 和 <select> 这类表单元素会维持自身状态&…

2026年最好用的六大代理IP服务商推荐:IPIDEA、Decodo、Smartproxy、IPRoyal、SOAX、Oxylabs

一、IPIDEA&#xff1a;IPIDEA是一家面向企业级AI训练与数据采集场景的全球代理服务商&#xff0c;其技术架构以超1亿高质量代理IP组成的庞大资源池为核心&#xff0c;覆盖全球220多个国家和地区&#xff0c;提供动态/静态住宅代理、移动代理及数据中心代理等全类型服务。IPIDE…

indirectly_readable和indirectly_writable这两个核心概念的具体定义和使用场景是什么?

indirectly_readable 和 indirectly_writable 是 C20 中引入的两个核心概念&#xff0c;它们为迭代器操作提供了基础的类型安全约束&#xff0c;是理解现代 C 范围库和算法库的关键。 下面这个表格能帮你快速把握它们的核心区别&#xff1a;特性indirectly_readableindirectly_…

超绝好用清理软件工具,(附下载方式)超多好用功能

电脑用久了&#xff0c;难免会堆积闲置软件、捆绑程序&#xff0c;自带卸载工具往往 “卸不干净”&#xff0c;残留的文件碎片和注册表垃圾不仅占用磁盘空间&#xff0c;还可能拖慢系统运行。今天给大家实测推荐三款 Windows 平台口碑爆棚的卸载工具 ——HiBit Uninstaller、Ge…

数据库分片和分区

1.分片Database Sharding是一种 水平拆分&#xff08;Horizontal Partitioning&#xff0c;按行拆分&#xff09; 的方案&#xff0c;用来解决单机数据库在数据量、并发量上的瓶颈问题。&#xff08;垂直拆分是指按列的字段拆分。&#xff09;一句话定义&#xff1a;把同一张逻…

何为Transformer 架构?

何为Transformer 架构&#xff1f;最后 选择AI大模型就是选择未来&#xff01;最近两年&#xff0c;大家都可以看到AI的发展有多快&#xff0c;时代在瞬息万变&#xff0c;我们又为何不给自己多一个选择&#xff0c;多一个出路&#xff0c;多一个可能呢&#xff1f; 与其在传统…

HiBit Uninstaller全攻略:轻量无广,彻底告别软件残留(附下载方式)

一、软件简介&#xff1a;为何它能成为系统清理神器&#xff1f;HiBit Uninstaller是由HiBit Software开发的免费Windows系统卸载工具&#xff0c;自2017年发布以来&#xff0c;凭借轻量、高效、无广告的特性&#xff0c;在同类工具中脱颖而出&#xff0c;成为普通用户与IT维护…

电阻的基础知识

电阻与电容、电感一样都是最基本的元器件&#xff0c;大量使用于各种电气或电子设备中。对从事电气工作的人而言或许过于普通&#xff0c;平时忽视了它&#xff0c;但如果没有电阻&#xff0c;电气或电子电路就无法建立。电阻就是如此重要的元器件。 电阻的作用电阻是具有一定电…

jvm性能调优

JVM性能调优方法调整堆内存大小 -Xms和-Xmx参数分别设置JVM堆的初始大小和最大大小。建议将两者设为相同值以避免运行时动态调整带来的性能开销。例如&#xff1a; -Xms4g -Xmx4g 根据应用需求调整大小&#xff0c;避免频繁Full GC。选择合适的垃圾回收器 根据应用特点选择垃圾…

【光子AI】FastAPI 极简权威指南 (The Ultimate Minimalist Guide)

这是一个 FastAPI 深度极简教程。虽然篇幅受限于输出长度,但这篇指南涵盖了从入门到生产环境部署的核心知识体系,内容密度相当于一本 10,000 字的技术手册的精华浓缩版。 我们将从零开始,逐步构建一个现代化的、高性能的 Web API。 FastAPI 极简权威指南 (The Ultimate Min…