Commit 7b35b16240040fe882344c414944a0f2f8e45acb

Authored by YunaiV
1 parent 28f54791

feat(bpm): 支持流程评论接口

- 新增流程评论创建、列表查询接口
- 封装 BpmCommentService 统一复用 Flowable Comment
- 评论列表返回评论人和对应任务信息
- 新增 BPM 评论类型字典与评论枚举
- 补充任务校验与历史任务 Map 查询能力
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 8 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
9 9 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
10 10 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
  11 +import cn.iocoder.yudao.module.bpm.enums.task.BpmAttachmentTypeEnum;
11 12 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
12 13 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
13 14 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
... ... @@ -24,12 +25,14 @@ import jakarta.validation.Valid;
24 25 import org.flowable.bpmn.model.UserTask;
25 26 import org.flowable.engine.history.HistoricProcessInstance;
26 27 import org.flowable.engine.runtime.ProcessInstance;
  28 +import org.flowable.engine.task.Attachment;
27 29 import org.flowable.task.api.Task;
28 30 import org.flowable.task.api.history.HistoricTaskInstance;
29 31 import org.springframework.security.access.prepost.PreAuthorize;
30 32 import org.springframework.validation.annotation.Validated;
31 33 import org.springframework.web.bind.annotation.*;
32 34  
  35 +import java.util.ArrayList;
33 36 import java.util.Collections;
34 37 import java.util.List;
35 38 import java.util.Map;
... ... @@ -95,7 +98,9 @@ public class BpmTaskController {
95 98 convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
96 99 Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
97 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 106 @GetMapping("manager-page")
... ... @@ -118,7 +123,9 @@ public class BpmTaskController {
118 123 convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
119 124 Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
120 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 131 @GetMapping("/list-by-process-instance-id")
... ... @@ -141,8 +148,9 @@ public class BpmTaskController {
141 148 // 获得 Form Map
142 149 Map<Long, BpmFormDO> formMap = formService.getFormMap(
143 150 convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));
  151 + Map<String, List<Attachment>> taskAttachmentMap = getTaskAttachmentMap(taskList);
144 152 return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList,
145   - formMap, userMap, deptMap));
  153 + formMap, userMap, deptMap, taskAttachmentMap));
146 154 }
147 155  
148 156 @PutMapping("/approve")
... ... @@ -244,4 +252,22 @@ public class BpmTaskController {
244 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 35 @Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
36 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 44 @Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
39 45 @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser
40 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 18 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
19 19 import org.flowable.engine.history.HistoricProcessInstance;
20 20 import org.flowable.engine.runtime.ProcessInstance;
  21 +import org.flowable.engine.task.Attachment;
21 22 import org.flowable.task.api.Task;
22 23 import org.flowable.task.api.history.HistoricTaskInstance;
23 24 import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
... ... @@ -64,10 +65,12 @@ public interface BpmTaskConvert {
64 65 Map<String, HistoricProcessInstance> processInstanceMap,
65 66 Map<Long, AdminUserRespDTO> userMap,
66 67 Map<Long, DeptRespDTO> deptMap,
67   - Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {
  68 + Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,
  69 + Map<String, List<Attachment>> taskAttachmentMap) {
68 70 List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> {
69 71 BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
70 72 taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
  73 + buildTaskEvidence(taskVO, task, taskAttachmentMap);
71 74 // 用户信息
72 75 AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
73 76 if (assignUser != null) {
... ... @@ -92,7 +95,8 @@ public interface BpmTaskConvert {
92 95 default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,
93 96 Map<Long, BpmFormDO> formMap,
94 97 Map<Long, AdminUserRespDTO> userMap,
95   - Map<Long, DeptRespDTO> deptMap) {
  98 + Map<Long, DeptRespDTO> deptMap,
  99 + Map<String, List<Attachment>> taskAttachmentMap) {
96 100 return CollectionUtils.convertList(taskList, task -> {
97 101 // 特殊:已取消的任务,不返回
98 102 BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
... ... @@ -101,6 +105,7 @@ public interface BpmTaskConvert {
101 105 return null;
102 106 }
103 107 taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task));
  108 + buildTaskEvidence(taskVO, task, taskAttachmentMap);
104 109 // 表单信息
105 110 BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class);
106 111 if (form != null) {
... ... @@ -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 219 * <p>
201 220 * 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。
... ...