Commit 7b35b16240040fe882344c414944a0f2f8e45acb
1 parent
28f54791
feat(bpm): 支持流程评论接口
- 新增流程评论创建、列表查询接口 - 封装 BpmCommentService 统一复用 Flowable Comment - 评论列表返回评论人和对应任务信息 - 新增 BPM 评论类型字典与评论枚举 - 补充任务校验与历史任务 Map 查询能力
Showing
3 changed files
with
56 additions
and
5 deletions
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
| @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | ||
| 8 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; | 8 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 9 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; | 9 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 10 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; | 10 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; |
| 11 | +import cn.iocoder.yudao.module.bpm.enums.task.BpmAttachmentTypeEnum; | ||
| 11 | import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; | 12 | import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; |
| 12 | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; | 13 | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| 13 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | 14 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; |
| @@ -24,12 +25,14 @@ import jakarta.validation.Valid; | @@ -24,12 +25,14 @@ import jakarta.validation.Valid; | ||
| 24 | import org.flowable.bpmn.model.UserTask; | 25 | import org.flowable.bpmn.model.UserTask; |
| 25 | import org.flowable.engine.history.HistoricProcessInstance; | 26 | import org.flowable.engine.history.HistoricProcessInstance; |
| 26 | import org.flowable.engine.runtime.ProcessInstance; | 27 | import org.flowable.engine.runtime.ProcessInstance; |
| 28 | +import org.flowable.engine.task.Attachment; | ||
| 27 | import org.flowable.task.api.Task; | 29 | import org.flowable.task.api.Task; |
| 28 | import org.flowable.task.api.history.HistoricTaskInstance; | 30 | import org.flowable.task.api.history.HistoricTaskInstance; |
| 29 | import org.springframework.security.access.prepost.PreAuthorize; | 31 | import org.springframework.security.access.prepost.PreAuthorize; |
| 30 | import org.springframework.validation.annotation.Validated; | 32 | import org.springframework.validation.annotation.Validated; |
| 31 | import org.springframework.web.bind.annotation.*; | 33 | import org.springframework.web.bind.annotation.*; |
| 32 | 34 | ||
| 35 | +import java.util.ArrayList; | ||
| 33 | import java.util.Collections; | 36 | import java.util.Collections; |
| 34 | import java.util.List; | 37 | import java.util.List; |
| 35 | import java.util.Map; | 38 | import java.util.Map; |
| @@ -95,7 +98,9 @@ public class BpmTaskController { | @@ -95,7 +98,9 @@ public class BpmTaskController { | ||
| 95 | convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); | 98 | convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); |
| 96 | Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( | 99 | Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( |
| 97 | convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); | 100 | convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); |
| 98 | - return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap)); | 101 | + Map<String, List<Attachment>> taskAttachmentMap = getTaskAttachmentMap(pageResult.getList()); |
| 102 | + return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, | ||
| 103 | + processDefinitionInfoMap, taskAttachmentMap)); | ||
| 99 | } | 104 | } |
| 100 | 105 | ||
| 101 | @GetMapping("manager-page") | 106 | @GetMapping("manager-page") |
| @@ -118,7 +123,9 @@ public class BpmTaskController { | @@ -118,7 +123,9 @@ public class BpmTaskController { | ||
| 118 | convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); | 123 | convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); |
| 119 | Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( | 124 | Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( |
| 120 | convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); | 125 | convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); |
| 121 | - return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap)); | 126 | + Map<String, List<Attachment>> taskAttachmentMap = getTaskAttachmentMap(pageResult.getList()); |
| 127 | + return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, | ||
| 128 | + processDefinitionInfoMap, taskAttachmentMap)); | ||
| 122 | } | 129 | } |
| 123 | 130 | ||
| 124 | @GetMapping("/list-by-process-instance-id") | 131 | @GetMapping("/list-by-process-instance-id") |
| @@ -141,8 +148,9 @@ public class BpmTaskController { | @@ -141,8 +148,9 @@ public class BpmTaskController { | ||
| 141 | // 获得 Form Map | 148 | // 获得 Form Map |
| 142 | Map<Long, BpmFormDO> formMap = formService.getFormMap( | 149 | Map<Long, BpmFormDO> formMap = formService.getFormMap( |
| 143 | convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey()))); | 150 | convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey()))); |
| 151 | + Map<String, List<Attachment>> taskAttachmentMap = getTaskAttachmentMap(taskList); | ||
| 144 | return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, | 152 | return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, |
| 145 | - formMap, userMap, deptMap)); | 153 | + formMap, userMap, deptMap, taskAttachmentMap)); |
| 146 | } | 154 | } |
| 147 | 155 | ||
| 148 | @PutMapping("/approve") | 156 | @PutMapping("/approve") |
| @@ -244,4 +252,22 @@ public class BpmTaskController { | @@ -244,4 +252,22 @@ public class BpmTaskController { | ||
| 244 | return success(BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap)); | 252 | return success(BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap)); |
| 245 | } | 253 | } |
| 246 | 254 | ||
| 255 | + /** | ||
| 256 | + * 获得任务附件 Map | ||
| 257 | + * | ||
| 258 | + * @param taskList 历史任务列表 | ||
| 259 | + * @return 任务附件 Map,key 为任务编号 | ||
| 260 | + */ | ||
| 261 | + private Map<String, List<Attachment>> getTaskAttachmentMap(List<HistoricTaskInstance> taskList) { | ||
| 262 | + // 1.1 获得流程实例的任务 Map | ||
| 263 | + Map<String, List<HistoricTaskInstance>> processTaskMap = convertMultiMap(taskList, | ||
| 264 | + HistoricTaskInstance::getProcessInstanceId); | ||
| 265 | + // 1.2 获得任务附件列表 | ||
| 266 | + List<Attachment> attachments = new ArrayList<>(); | ||
| 267 | + processTaskMap.forEach((processInstanceId, tasks) -> attachments.addAll(taskService.getAttachments( | ||
| 268 | + processInstanceId, convertSet(tasks, HistoricTaskInstance::getId), BpmAttachmentTypeEnum.TASK_ATTACHMENT))); | ||
| 269 | + // 2. 返回 Map | ||
| 270 | + return convertMultiMap(attachments, Attachment::getTaskId); | ||
| 271 | + } | ||
| 272 | + | ||
| 247 | } | 273 | } |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
| @@ -35,6 +35,12 @@ public class BpmTaskRespVO { | @@ -35,6 +35,12 @@ public class BpmTaskRespVO { | ||
| 35 | @Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") | 35 | @Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") |
| 36 | private String reason; | 36 | private String reason; |
| 37 | 37 | ||
| 38 | + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") | ||
| 39 | + private String signPicUrl; | ||
| 40 | + | ||
| 41 | + @Schema(description = "附件", example = "[https://test.yudao.iocoder.cn/20260609/test.txt]") | ||
| 42 | + private List<String> attachments; | ||
| 43 | + | ||
| 38 | @Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") | 44 | @Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") |
| 39 | @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser | 45 | @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser |
| 40 | private Long owner; | 46 | private Long owner; |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
| @@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | @@ -18,6 +18,7 @@ import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | ||
| 18 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 18 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 19 | import org.flowable.engine.history.HistoricProcessInstance; | 19 | import org.flowable.engine.history.HistoricProcessInstance; |
| 20 | import org.flowable.engine.runtime.ProcessInstance; | 20 | import org.flowable.engine.runtime.ProcessInstance; |
| 21 | +import org.flowable.engine.task.Attachment; | ||
| 21 | import org.flowable.task.api.Task; | 22 | import org.flowable.task.api.Task; |
| 22 | import org.flowable.task.api.history.HistoricTaskInstance; | 23 | import org.flowable.task.api.history.HistoricTaskInstance; |
| 23 | import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; | 24 | import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; |
| @@ -64,10 +65,12 @@ public interface BpmTaskConvert { | @@ -64,10 +65,12 @@ public interface BpmTaskConvert { | ||
| 64 | Map<String, HistoricProcessInstance> processInstanceMap, | 65 | Map<String, HistoricProcessInstance> processInstanceMap, |
| 65 | Map<Long, AdminUserRespDTO> userMap, | 66 | Map<Long, AdminUserRespDTO> userMap, |
| 66 | Map<Long, DeptRespDTO> deptMap, | 67 | Map<Long, DeptRespDTO> deptMap, |
| 67 | - Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) { | 68 | + Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap, |
| 69 | + Map<String, List<Attachment>> taskAttachmentMap) { | ||
| 68 | List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> { | 70 | List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> { |
| 69 | BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); | 71 | BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); |
| 70 | taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)); | 72 | taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)); |
| 73 | + buildTaskEvidence(taskVO, task, taskAttachmentMap); | ||
| 71 | // 用户信息 | 74 | // 用户信息 |
| 72 | AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); | 75 | AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); |
| 73 | if (assignUser != null) { | 76 | if (assignUser != null) { |
| @@ -92,7 +95,8 @@ public interface BpmTaskConvert { | @@ -92,7 +95,8 @@ public interface BpmTaskConvert { | ||
| 92 | default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList, | 95 | default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList, |
| 93 | Map<Long, BpmFormDO> formMap, | 96 | Map<Long, BpmFormDO> formMap, |
| 94 | Map<Long, AdminUserRespDTO> userMap, | 97 | Map<Long, AdminUserRespDTO> userMap, |
| 95 | - Map<Long, DeptRespDTO> deptMap) { | 98 | + Map<Long, DeptRespDTO> deptMap, |
| 99 | + Map<String, List<Attachment>> taskAttachmentMap) { | ||
| 96 | return CollectionUtils.convertList(taskList, task -> { | 100 | return CollectionUtils.convertList(taskList, task -> { |
| 97 | // 特殊:已取消的任务,不返回 | 101 | // 特殊:已取消的任务,不返回 |
| 98 | BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); | 102 | BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); |
| @@ -101,6 +105,7 @@ public interface BpmTaskConvert { | @@ -101,6 +105,7 @@ public interface BpmTaskConvert { | ||
| 101 | return null; | 105 | return null; |
| 102 | } | 106 | } |
| 103 | taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task)); | 107 | taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task)); |
| 108 | + buildTaskEvidence(taskVO, task, taskAttachmentMap); | ||
| 104 | // 表单信息 | 109 | // 表单信息 |
| 105 | BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class); | 110 | BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class); |
| 106 | if (form != null) { | 111 | if (form != null) { |
| @@ -196,6 +201,20 @@ public interface BpmTaskConvert { | @@ -196,6 +201,20 @@ public interface BpmTaskConvert { | ||
| 196 | } | 201 | } |
| 197 | 202 | ||
| 198 | /** | 203 | /** |
| 204 | + * 构建任务审批证据 | ||
| 205 | + * | ||
| 206 | + * @param taskVO 任务 VO | ||
| 207 | + * @param task 历史任务 | ||
| 208 | + * @param taskAttachmentMap 任务附件 Map | ||
| 209 | + */ | ||
| 210 | + default void buildTaskEvidence(BpmTaskRespVO taskVO, HistoricTaskInstance task, | ||
| 211 | + Map<String, List<Attachment>> taskAttachmentMap) { | ||
| 212 | + taskVO.setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task)); | ||
| 213 | + taskVO.setAttachments(convertList( | ||
| 214 | + taskAttachmentMap != null ? taskAttachmentMap.get(task.getId()) : null, Attachment::getUrl)); | ||
| 215 | + } | ||
| 216 | + | ||
| 217 | + /** | ||
| 199 | * 将父任务的属性,拷贝到子任务(加签任务) | 218 | * 将父任务的属性,拷贝到子任务(加签任务) |
| 200 | * <p> | 219 | * <p> |
| 201 | * 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。 | 220 | * 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。 |