Commit 8f2167892735caaeba921509a59fc1f4a4653e65

Authored by 颜惠青
1 parent 4fa0fd51

工作流 底层扩展 支持 app端 pc端 审批附件url功能

Showing 14 changed files with 233 additions and 1 deletions
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
... ... @@ -107,6 +107,10 @@ public class BpmApprovalDetailRespVO {
107 107 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
108 108 private String signPicUrl;
109 109  
  110 + // add by yanhuiqing 扩展审批图片
  111 + @Schema(description = "审批附件url", example = "https://www.iocoder.cn/sign.png")
  112 + private List<String> attattmentUrls;
  113 +
110 114 }
111 115  
112 116 }
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
... ... @@ -21,6 +21,10 @@ public class BpmTaskApproveReqVO {
21 21 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
22 22 private String signPicUrl;
23 23  
  24 + // add by yanhuiqing 扩展审批图片
  25 + @Schema(description = "审批附件url", example = "https://www.iocoder.cn/sign.png")
  26 + private List<String> attattmentUrls;
  27 +
24 28 @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
25 29 private Map<String, Object> variables;
26 30  
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
... ... @@ -35,6 +35,10 @@ public class BpmTaskRespVO {
35 35 @Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
36 36 private String reason;
37 37  
  38 + // add by yanhuiqing 扩展审批图片
  39 + @Schema(description = "审批附件url", example = "https://www.iocoder.cn/sign.png")
  40 + private List<String> attattmentUrls;
  41 +
38 42 @Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
39 43 @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser
40 44 private Long owner;
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java
... ... @@ -5,6 +5,8 @@ import lombok.Data;
5 5  
6 6 import jakarta.validation.constraints.NotEmpty;
7 7  
  8 +import java.util.List;
  9 +
8 10 @Schema(description = "管理后台 - 退回流程任务的 Request VO")
9 11 @Data
10 12 public class BpmTaskReturnReqVO {
... ... @@ -21,4 +23,8 @@ public class BpmTaskReturnReqVO {
21 23 @NotEmpty(message = "退回意见不能为空")
22 24 private String reason;
23 25  
  26 + // add by yanhuiqing 扩展审批图片
  27 + @Schema(description = "审批附件url", example = "https://www.iocoder.cn/sign.png")
  28 + private List<String> attattmentUrls;
  29 +
24 30 }
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/convert/task/BpmProcessInstanceConvert.java
... ... @@ -206,7 +206,8 @@ public interface BpmProcessInstanceConvert {
206 206 }
207 207 return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
208 208 .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
209   - .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));
  209 + .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task))
  210 + .setAttattmentUrls(FlowableUtils.getTaskAttachPicUrl(task));
210 211 }
211 212  
212 213 default Set<Long> parseUserIds(HistoricProcessInstance processInstance,
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java
... ... @@ -105,4 +105,9 @@ public class BpmnVariableConstants {
105 105 */
106 106 public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL";
107 107  
  108 + /**
  109 + * 审批附件 - 图片 URL
  110 + */
  111 + public static final String TASK_APPROVE_PIC_URL = "TASK_APPROVE_PIC_URL";
  112 +
108 113 }
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/framework/flowable/core/util/FlowableUtils.java
... ... @@ -24,6 +24,7 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
24 24 import org.flowable.engine.impl.util.CommandContextUtil;
25 25 import org.flowable.engine.runtime.ProcessInstance;
26 26 import org.flowable.task.api.TaskInfo;
  27 +import org.flowable.task.api.history.HistoricTaskInstance;
27 28  
28 29 import java.util.HashMap;
29 30 import java.util.List;
... ... @@ -359,4 +360,12 @@ public class FlowableUtils {
359 360 return getExpressionValue(variableContainer, expressionString);
360 361 }
361 362  
  363 + /**
  364 + * add by yanhuiqing 新增获取审批任务附件图片
  365 + * @param task
  366 + * @return
  367 + */
  368 + public static List<String> getTaskAttachPicUrl(TaskInfo task) {
  369 + return (List<String>) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_APPROVE_PIC_URL);
  370 + }
362 371 }
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
... ... @@ -212,6 +212,54 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
212 212 historicProcessInstance, processInstanceStatus, activities, tasks);
213 213 runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables,
214 214 activities, tasks);
  215 + /**
  216 + * // ========== 新增:处理附件URL列表 add by yanhuiqing==========
  217 + */
  218 +
  219 + // 步骤1:获取流程实例ID(转为字符串,适配Flowable的String类型ID)
  220 + String processInstanceId = reqVO.getProcessInstanceId() != null ? reqVO.getProcessInstanceId().toString() : null;
  221 +
  222 + // 步骤2:处理已结束节点的tasks附件URL
  223 + if (CollUtil.isNotEmpty(runActivityNodes)) {
  224 + runActivityNodes.forEach(activityNode -> {
  225 + if (CollUtil.isNotEmpty(activityNode.getTasks())) {
  226 + activityNode.getTasks().forEach(task -> {
  227 + // 传入流程实例ID和任务ID,查询附件URL
  228 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, task.getId());
  229 + task.setAttattmentUrls(urlList); // 为task设置附件URL列表
  230 + });
  231 + }
  232 + });
  233 + }
  234 + if (CollUtil.isNotEmpty(endActivityNodes)) {
  235 + endActivityNodes.forEach(activityNode -> {
  236 + if (CollUtil.isNotEmpty(activityNode.getTasks())) {
  237 + activityNode.getTasks().forEach(task -> {
  238 + // 传入流程实例ID和任务ID,查询附件URL
  239 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, task.getId());
  240 + task.setAttattmentUrls(urlList); // 为task设置附件URL列表
  241 + });
  242 + }
  243 + });
  244 + }
  245 +
  246 + // 步骤3:处理运行中节点的tasks附件URL
  247 + if (CollUtil.isNotEmpty(runActivityNodes)) {
  248 + runActivityNodes.forEach(activityNode -> {
  249 + if (CollUtil.isNotEmpty(activityNode.getTasks())) {
  250 + activityNode.getTasks().forEach(task -> {
  251 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, task.getId());
  252 + task.setAttattmentUrls(urlList);
  253 + });
  254 + }
  255 + });
  256 + }
  257 +
  258 + /**
  259 + * // ========== end 新增:处理附件URL列表 add by yanhuiqing==========
  260 + */
  261 +
  262 +
215 263 }
216 264  
217 265 // 2.2 流程已经结束,直接 return,无需预测
... ... @@ -242,6 +290,69 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
242 290 List<ActivityNode> simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel,
243 291 processDefinitionInfo,
244 292 processVariables, activities, needSimulateTaskDefKeysByReturn);
  293 + /**
  294 + * // ========== 新增:处理附件URL列表 add by yanhuiqing==========
  295 + */
  296 +
  297 + // 步骤1:获取流程实例ID(转为字符串,适配Flowable的String类型ID)
  298 + String processInstanceId = reqVO.getProcessInstanceId() != null ? reqVO.getProcessInstanceId().toString() : null;
  299 +
  300 + // 步骤2:处理已结束节点的tasks附件URL
  301 + if (CollUtil.isNotEmpty(runActivityNodes)) {
  302 + runActivityNodes.forEach(activityNode -> {
  303 + if (CollUtil.isNotEmpty(activityNode.getTasks())) {
  304 + activityNode.getTasks().forEach(task -> {
  305 + // 传入流程实例ID和任务ID,查询附件URL
  306 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, task.getId());
  307 + task.setAttattmentUrls(urlList); // 为task设置附件URL列表
  308 + });
  309 + }
  310 + });
  311 + }
  312 + if (CollUtil.isNotEmpty(endActivityNodes)) {
  313 + endActivityNodes.forEach(activityNode -> {
  314 + if (CollUtil.isNotEmpty(activityNode.getTasks())) {
  315 + activityNode.getTasks().forEach(task -> {
  316 + // 传入流程实例ID和任务ID,查询附件URL
  317 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, task.getId());
  318 + task.setAttattmentUrls(urlList); // 为task设置附件URL列表
  319 + });
  320 + }
  321 + });
  322 + }
  323 +
  324 + // 步骤3:处理运行中节点的tasks附件URL
  325 + if (CollUtil.isNotEmpty(runActivityNodes)) {
  326 + runActivityNodes.forEach(activityNode -> {
  327 + if (CollUtil.isNotEmpty(activityNode.getTasks())) {
  328 + activityNode.getTasks().forEach(task -> {
  329 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, task.getId());
  330 + task.setAttattmentUrls(urlList);
  331 + });
  332 + }
  333 + });
  334 + }
  335 +
  336 + // 步骤4:处理模拟节点的tasks附件URL(如果有)
  337 + if (CollUtil.isNotEmpty(simulateActivityNodes)) {
  338 + simulateActivityNodes.forEach(activityNode -> {
  339 + if (CollUtil.isNotEmpty(activityNode.getTasks())) {
  340 + activityNode.getTasks().forEach(task -> {
  341 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, task.getId());
  342 + task.setAttattmentUrls(urlList);
  343 + });
  344 + }
  345 + });
  346 + }
  347 +
  348 + // 步骤5:处理待办任务的附件URL
  349 + if (todoTask != null) {
  350 + List<String> urlList = taskService.getAttachmentUrlList(processInstanceId, todoTask.getId());
  351 + todoTask.setAttattmentUrls(urlList);;
  352 + }
  353 + /**
  354 + * // ========== end 新增:处理附件URL列表 add by yanhuiqing==========
  355 + */
245 356  
246 357 // 4. 拼接最终数据
247 358 return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance,
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/service/task/BpmTaskService.java
... ... @@ -7,6 +7,7 @@ import com.zteits.urbanops.module.bpm.enums.definition.BpmUserTaskTimeoutHandler
7 7 import jakarta.validation.Valid;
8 8 import org.flowable.bpmn.model.UserTask;
9 9 import org.flowable.engine.history.HistoricActivityInstance;
  10 +import org.flowable.engine.task.Attachment;
10 11 import org.flowable.task.api.Task;
11 12 import org.flowable.task.api.TaskInfo;
12 13 import org.flowable.task.api.history.HistoricTaskInstance;
... ... @@ -329,4 +330,16 @@ public interface BpmTaskService {
329 330 */
330 331 void triggerTask(String processInstanceId, String taskDefineKey);
331 332  
  333 +
  334 +
  335 + //---------------------------------------------------------------
  336 + //扩展芋道 bpm不支持,审批时上传的附件问题 add by yanhuiqing
  337 +
  338 + /**
  339 + * 扩展 功能支持 审批人上传的附件类型
  340 + * @param taskId
  341 + * @param processInstanceId
  342 + * @return
  343 + */
  344 + List<String> getAttachmentUrlList(String processInstanceId, String taskId);
332 345 }
... ...
urbanops-module-bpm/src/main/java/com/zteits/urbanops/module/bpm/service/task/BpmTaskServiceImpl.java
... ... @@ -49,6 +49,7 @@ import org.flowable.engine.history.HistoricActivityInstance;
49 49 import org.flowable.engine.runtime.ActivityInstance;
50 50 import org.flowable.engine.runtime.Execution;
51 51 import org.flowable.engine.runtime.ProcessInstance;
  52 +import org.flowable.engine.task.Attachment;
52 53 import org.flowable.task.api.DelegationState;
53 54 import org.flowable.task.api.Task;
54 55 import org.flowable.task.api.TaskInfo;
... ... @@ -63,6 +64,7 @@ import org.springframework.transaction.support.TransactionSynchronization;
63 64 import org.springframework.transaction.support.TransactionSynchronizationManager;
64 65  
65 66 import java.util.*;
  67 +import java.util.stream.Collectors;
66 68 import java.util.stream.Stream;
67 69  
68 70 import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -586,6 +588,27 @@ public class BpmTaskServiceImpl implements BpmTaskService {
586 588 // 2.2 添加评论
587 589 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
588 590 BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
  591 + /**
  592 + * add by yanhuiqing 审批附件 begin
  593 + */
  594 +
  595 + List<String> urlList = reqVO.getAttattmentUrls();
  596 + if(ObjectUtil.isNotEmpty(urlList)){
  597 + String targetUrls = CollUtil.isEmpty(urlList)?StrUtil.EMPTY:urlList.stream()
  598 + .filter(StrUtil::isNotEmpty)
  599 + .distinct()
  600 + .collect(Collectors.joining(","));
  601 + String attachmentName = "审批附件-" + BpmCommentTypeEnum.APPROVE.getType();
  602 + String attachmentType = "URL"; // 附件类型标记为URL(可自定义)
  603 + String description = BpmCommentTypeEnum.APPROVE.getName()+"的附件";
  604 + taskService.createAttachment(attachmentType,task.getId(),task.getProcessInstanceId(),attachmentName,description,targetUrls);
  605 + taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_APPROVE_PIC_URL, reqVO.getAttattmentUrls());
  606 +
  607 + }
  608 + /**
  609 + * add by yanhuiqing 审批附件 end
  610 + */
  611 +
589 612  
590 613 // 3. 设置流程变量。如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时 variables 一定会为空
591 614 // 场景一:A 节点发起,B 节点表单无可编辑字段,审批通过时,C 节点需要流程变量获取下一个执行节点,但因为 B 节点无可编辑的字段,variables 为空,流程可能出现问题。
... ... @@ -929,6 +952,24 @@ public class BpmTaskServiceImpl implements BpmTaskService {
929 952 // 2.1.1 添加评论
930 953 taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(),
931 954 BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason()));
  955 + /**
  956 + * add by yanhuiqing 审批附件 begin
  957 + */
  958 + List<String> urlList = reqVO.getAttattmentUrls();
  959 + if(ObjectUtils.isNotAllEmpty()){
  960 + String targetUrls = CollUtil.isEmpty(urlList)?StrUtil.EMPTY:urlList.stream()
  961 + .filter(StrUtil::isNotEmpty)
  962 + .distinct()
  963 + .collect(Collectors.joining(","));
  964 + String attachmentName = "审批附件-" + BpmCommentTypeEnum.RETURN.getType();
  965 + String attachmentType = "URL"; // 附件类型标记为URL(可自定义)
  966 + String description = BpmCommentTypeEnum.RETURN.getName()+"的附件";
  967 + taskService.createAttachment(attachmentType,task.getId(),task.getProcessInstanceId(),attachmentName,description,targetUrls);
  968 + }
  969 + /**
  970 + * add by yanhuiqing 审批附件 end
  971 + */
  972 +
932 973 // 2.1.2 更新 task 状态 + 原因
933 974 updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason());
934 975 } else { // 情况二:别人的任务,进行 CANCEL 标记
... ... @@ -1643,6 +1684,28 @@ public class BpmTaskServiceImpl implements BpmTaskService {
1643 1684 FlowableUtils.execute(execution.getTenantId(),
1644 1685 () -> runtimeService.trigger(execution.getId()));
1645 1686 }
  1687 + //扩展 add by yanhuiqing
  1688 + @Override
  1689 + public List<String> getAttachmentUrlList(String processInstanceId, String taskId) {
  1690 +
  1691 + List<Attachment> attachmentList = taskService.getProcessInstanceAttachments(processInstanceId);
  1692 + // 空列表直接返回空集合(避免null)
  1693 + if (CollUtil.isEmpty(attachmentList)) {
  1694 + return CollUtil.empty(String.class);
  1695 + }
  1696 +
  1697 + List<String> targetList = new ArrayList<>();
  1698 + for(Attachment a : attachmentList){
  1699 + if(taskId.equals(a.getTaskId())){
  1700 + String urls = a.getUrl();
  1701 + String[] array = urls.split(",");
  1702 + targetList.addAll(Arrays.asList(array));
  1703 + }
  1704 + }
  1705 + return targetList.stream()
  1706 + .distinct() // 核心:去重
  1707 + .collect(Collectors.toList());
  1708 + }
1646 1709  
1647 1710 /**
1648 1711 * 获得自身的代理对象,解决 AOP 生效问题
... ...
urbanops-module-workorder/src/main/java/com/zteits/urbanops/module/workorder/controller/app/garden/AppGardenWorkOrderController.java
... ... @@ -303,6 +303,8 @@ public class AppGardenWorkOrderController {
303 303 rejReqVO.setCurTaskKey(reqVO.getTaskKey());
304 304 rejReqVO.setReason(reqVO.getReason());
305 305 rejReqVO.setWorkerDataId(reqVO.getWorkerDataId());
  306 + List<String> returnImgs = reqVO.getReturnImgs();
  307 + rejReqVO.setReturnImgs(returnImgs);
306 308 this.returnPreviousNode(rejReqVO);
307 309 log.info("当前操作:"+OperateTypeEnum.YL_TEAM_LEADER_CONFIRM_REJECT.getDesc()+"<--结束");
308 310 return success(true);
... ... @@ -335,6 +337,7 @@ public class AppGardenWorkOrderController {
335 337 returnApproveReqVO.setAgree(approveStatus);
336 338 returnApproveReqVO.setReason(reqVO.getReason());
337 339 returnApproveReqVO.setId(reqVO.getTaskId());
  340 + returnApproveReqVO.setReturnImgs(reqVO.getReturnImgs());
338 341 this.approveTask(returnApproveReqVO);
339 342 log.info("当前操作:"+OperateTypeEnum.YL_TEAM_LEADER_RETURN.getDesc()+"<--结束");
340 343 return success(true);
... ... @@ -379,6 +382,7 @@ public class AppGardenWorkOrderController {
379 382 inspectReqVO.setAgree(verifyStatus);
380 383 inspectReqVO.setReason(reqVO.getReason());
381 384 inspectReqVO.setId(reqVO.getTaskId());
  385 + inspectReqVO.setReturnImgs(reqVO.getReturnImgs());
382 386 this.approveTask(inspectReqVO);
383 387 log.info("当前操作:"+OperateTypeEnum.YL_INSPECTOR_START_FINISH.getDesc()+"<--结束");
384 388 return success(true);
... ...
urbanops-module-workorder/src/main/java/com/zteits/urbanops/module/workorder/controller/app/garden/vo/process/AppGardenApprovalDetailRespVO.java
... ... @@ -111,6 +111,9 @@ public class AppGardenApprovalDetailRespVO {
111 111  
112 112 @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
113 113 private String signPicUrl;
  114 + // add by yanhuiqing 扩展审批图片
  115 + @Schema(description = "审批附件url", example = "https://www.iocoder.cn/sign.png")
  116 + private List<String> attattmentUrls;
114 117  
115 118 }
116 119  
... ...
urbanops-module-workorder/src/main/java/com/zteits/urbanops/module/workorder/controller/app/garden/vo/task/AppGardenTaskApproveReqVO.java
... ... @@ -37,4 +37,7 @@ public class AppGardenTaskApproveReqVO {
37 37 //@NotEmpty(message = "工单的数据id不能为空")
38 38 private Long workerDataId;
39 39  
  40 + @Schema(description = "退回时的拍照图片")
  41 + private List<String> returnImgs;
  42 +
40 43 }
41 44 \ No newline at end of file
... ...
urbanops-module-workorder/src/main/java/com/zteits/urbanops/module/workorder/service/garden/BpmGardenServiceImpl.java
... ... @@ -229,6 +229,7 @@ public class BpmGardenServiceImpl implements BpmGardenService{
229 229 //TODO 审批候选人需要赋值
230 230 Map<String, List<Long>> nextAssignees = new HashMap<>();
231 231 targetVO.setNextAssignees(nextAssignees);
  232 + targetVO.setAttattmentUrls(reqVO.getReturnImgs());
232 233 taskService.approveTask(SecurityFrameworkUtils.getLoginUserId(),targetVO);
233 234 }
234 235  
... ... @@ -314,6 +315,7 @@ public class BpmGardenServiceImpl implements BpmGardenService{
314 315 String currentTaskey = reqVO.getCurTaskKey();
315 316 targetVO.setTargetTaskDefinitionKey(TaskNodeUtils.getPreviousNodeName(currentTaskey));
316 317 Long userId = SecurityFrameworkUtils.getLoginUserId();
  318 + targetVO.setAttattmentUrls(reqVO.getReturnImgs());
317 319 taskService.returnTask(userId, targetVO);
318 320  
319 321 }
... ...