Commit acbe083c7f0fa41fce13fd6c7744094002719992

Authored by Lesan
1 parent 311488fa

feat: 实现流程打印功能

yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
... ... @@ -196,4 +196,12 @@ public class BpmProcessInstanceController {
196 196 return success(processInstanceService.getProcessInstanceBpmnModelView(id));
197 197 }
198 198  
  199 + @GetMapping("/get-print-data")
  200 + @Operation(summary = "获得打印数据")
  201 + @Parameter(name = "id", description = "流程实例的编号", required = true)
  202 + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
  203 + public CommonResult<BpmProcessPrintDataRespVO> getPrintData(@RequestParam("processInstanceId") String processInstanceId) {
  204 + return success(processInstanceService.getPrintData(getLoginUserId(), processInstanceId));
  205 + }
  206 +
199 207 }
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +import java.util.List;
  7 +
  8 +
  9 +@Schema(description = "管理后台 - 打印数据 Response VO")
  10 +@Data
  11 +public class BpmProcessPrintDataRespVO {
  12 +
  13 + private Boolean printTemplateEnable;
  14 +
  15 + private Integer processStatus;
  16 +
  17 + private String processStatusShow;
  18 +
  19 + private String processInstanceId;
  20 +
  21 + private String processBusinessKey;
  22 +
  23 + private String processName;
  24 +
  25 + private String startUser;
  26 +
  27 + private String startUserDept;
  28 +
  29 + private String startTime;
  30 +
  31 + private List<ApproveNode> approveNodes;
  32 +
  33 + private List<FormField> formFields;
  34 +
  35 + private String printTemplateHtml;
  36 +
  37 + @Data
  38 + public static class ApproveNode {
  39 +
  40 + private String nodeName;
  41 +
  42 + private String nodeDesc;
  43 +
  44 + private String signUrl;
  45 +
  46 + private String nodeId;
  47 +
  48 + }
  49 +
  50 + @Data
  51 + public static class FormField {
  52 +
  53 + private String formId;
  54 +
  55 + private String formName;
  56 +
  57 + private String formType;
  58 +
  59 + private String formValue;
  60 +
  61 + private String formValueShow;
  62 +
  63 + }
  64 +
  65 +}
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java
1 1 package cn.iocoder.yudao.module.bpm.enums.task;
2 2  
  3 +import cn.hutool.core.util.ArrayUtil;
3 4 import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4 5 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
5 6 import lombok.AllArgsConstructor;
... ... @@ -47,4 +48,8 @@ public enum BpmProcessInstanceStatusEnum implements ArrayValuable&lt;Integer&gt; {
47 48 APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());
48 49 }
49 50  
  51 + public static BpmProcessInstanceStatusEnum valueOf(Integer status) {
  52 + return ArrayUtil.firstMatch(item -> item.getStatus().equals(status), values());
  53 + }
  54 +
50 55 }
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java
1 1 package cn.iocoder.yudao.module.bpm.enums.task;
2 2  
  3 +import cn.hutool.core.util.ArrayUtil;
3 4 import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
5 6 import lombok.AllArgsConstructor;
... ... @@ -68,4 +69,8 @@ public enum BpmTaskStatusEnum {
68 69 return ObjUtil.equal(status, CANCEL.getStatus());
69 70 }
70 71  
  72 + public static BpmTaskStatusEnum valueOf(Integer status) {
  73 + return ArrayUtil.firstMatch(item -> item.getStatus().equals(status), values());
  74 + }
  75 +
71 76 }
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
... ... @@ -113,6 +113,14 @@ public interface BpmProcessInstanceService {
113 113 */
114 114 BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id);
115 115  
  116 + /**
  117 + * 获取流程打印所需数据
  118 + * @param loginUserId 打印人
  119 + * @param processInstanceId 流程实例id
  120 + * @return 打印所需数据
  121 + */
  122 + BpmProcessPrintDataRespVO getPrintData(Long loginUserId, String processInstanceId);
  123 +
116 124 // ========== Update 写入相关方法 ==========
117 125  
118 126 /**
... ...
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
... ... @@ -2,9 +2,13 @@ package cn.iocoder.yudao.module.bpm.service.task;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.ListUtil;
  5 +import cn.hutool.core.date.DatePattern;
  6 +import cn.hutool.core.date.DateTime;
5 7 import cn.hutool.core.date.DateUtil;
6 8 import cn.hutool.core.lang.Assert;
7 9 import cn.hutool.core.util.*;
  10 +import cn.hutool.json.JSONObject;
  11 +import cn.hutool.json.JSONUtil;
8 12 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 13 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
10 14 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
... ... @@ -57,6 +61,10 @@ import org.flowable.engine.runtime.ProcessInstance;
57 61 import org.flowable.engine.runtime.ProcessInstanceBuilder;
58 62 import org.flowable.task.api.Task;
59 63 import org.flowable.task.api.history.HistoricTaskInstance;
  64 +import org.jsoup.Jsoup;
  65 +import org.jsoup.nodes.Document;
  66 +import org.jsoup.nodes.Element;
  67 +import org.jsoup.select.Elements;
60 68 import org.springframework.context.annotation.Lazy;
61 69 import org.springframework.stereotype.Service;
62 70 import org.springframework.transaction.annotation.Transactional;
... ... @@ -726,6 +734,141 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
726 734 userMap, deptMap);
727 735 }
728 736  
  737 + @Override
  738 + public BpmProcessPrintDataRespVO getPrintData(Long loginUserId, String processInstanceId) {
  739 + // TODO 方法抽离
  740 + // 流程实例
  741 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(processInstanceId);
  742 + if (historicProcessInstance == null) {
  743 + throw exception(PROCESS_INSTANCE_NOT_EXISTS);
  744 + }
  745 + // 准备数据
  746 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
  747 + .getProcessDefinitionInfo(historicProcessInstance.getProcessDefinitionId());
  748 + BpmModelMetaInfoVO.PrintTemplateSetting printTemplateSetting = processDefinitionInfo.getPrintTemplateSetting();
  749 + List<String> formFieldList = processDefinitionInfo.getFormFields();
  750 + List<JSONObject> formFieldObjList = formFieldList.stream().map(JSONUtil::parseObj).toList();
  751 + List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
  752 + .finished()
  753 + .includeTaskLocalVariables()
  754 + .processInstanceId(processInstanceId)
  755 + .taskVariableValueNotEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS,
  756 + BpmTaskStatusEnum.CANCEL.getStatus())
  757 + .orderByHistoricTaskInstanceStartTime().asc().list();
  758 + Set<Long> userIds = convertSet(tasks, item -> Long.valueOf(item.getAssignee()));
  759 + userIds.add(loginUserId);
  760 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
  761 + HashMap<String, String> printDataMap = new HashMap<>(8 + formFieldList.size());
  762 + // 返回打印所需数据
  763 + BpmProcessPrintDataRespVO printData = new BpmProcessPrintDataRespVO();
  764 + // 打印模板是否开启
  765 + printData.setPrintTemplateEnable(printTemplateSetting != null && Boolean.TRUE.equals(printTemplateSetting.getEnable()));
  766 + // 流程相关数据
  767 + printData.setProcessStatus(FlowableUtils.getProcessInstanceStatus(historicProcessInstance));
  768 + printData.setProcessStatusShow(BpmProcessInstanceStatusEnum.valueOf(printData.getProcessStatus()).getDesc());
  769 + printData.setProcessInstanceId(historicProcessInstance.getId());
  770 + printData.setProcessName(historicProcessInstance.getName());
  771 + printData.setProcessBusinessKey(historicProcessInstance.getBusinessKey());
  772 + printData.setStartTime(DatePattern.NORM_DATETIME_MINUTE_FORMAT.format(historicProcessInstance.getStartTime()));
  773 + // 发起人
  774 + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(historicProcessInstance.getStartUserId()));
  775 + DeptRespDTO dept = deptApi.getDept(startUser.getDeptId());
  776 + printData.setStartUser(startUser.getNickname());
  777 + printData.setStartUserDept(dept.getName());
  778 + // 审批历史
  779 + List<BpmProcessPrintDataRespVO.ApproveNode> approveNodes = new ArrayList<>(tasks.size());
  780 + tasks.forEach(item -> {
  781 + Map<String, Object> taskLocalVariables = item.getTaskLocalVariables();
  782 + BpmProcessPrintDataRespVO.ApproveNode approveNode = new BpmProcessPrintDataRespVO.ApproveNode();
  783 + approveNode.setNodeName(item.getName());
  784 + approveNode.setNodeId(item.getId());
  785 + approveNode.setSignUrl((String) taskLocalVariables.getOrDefault(BpmnVariableConstants.TASK_SIGN_PIC_URL, ""));
  786 + approveNode.setNodeDesc(StrUtil.format("{} / {} / {} / {} / {}",
  787 + userMap.get(Long.valueOf(item.getAssignee())).getNickname(),
  788 + item.getName(),
  789 + DateUtil.formatDateTime(item.getEndTime()),
  790 + BpmTaskStatusEnum.valueOf((Integer) taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_STATUS)).getName(),
  791 + taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_REASON)));
  792 + approveNodes.add(approveNode);
  793 + });
  794 + printData.setApproveNodes(approveNodes);
  795 + // 表单数据
  796 + Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();
  797 + List<BpmProcessPrintDataRespVO.FormField> formFields = new ArrayList<>(formFieldList.size());
  798 + formFieldObjList.forEach(item -> {
  799 + BpmProcessPrintDataRespVO.FormField formField = new BpmProcessPrintDataRespVO.FormField();
  800 + formField.setFormName(item.getStr("title"));
  801 + formField.setFormType(item.getStr("type"));
  802 + formField.setFormId(item.getStr("field"));
  803 + formField.setFormValue(processVariables.get(item.getStr("field")).toString());
  804 + // TODO 根据不同类型的表单展示,下面为图片和输入框示例
  805 + if (formField.getFormType().equals("input")) {
  806 + formField.setFormValueShow(processVariables.get(item.getStr("field")).toString());
  807 + } else if (formField.getFormType().equals("UploadImg")) {
  808 + formField.setFormValueShow("<img style=\"max-width: 600px\" src=\"" + processVariables.get(item.getStr("field")).toString() + "\" />");
  809 + } else {
  810 + formField.setFormValueShow("此类型表单展示未完善");
  811 + }
  812 + printDataMap.put(formField.getFormId(), formField.getFormValueShow());
  813 + formFields.add(formField);
  814 + });
  815 + printData.setFormFields(formFields);
  816 + // 数据映射
  817 + printDataMap.put("processName", printData.getProcessName());
  818 + printDataMap.put("printUsername", userMap.get(loginUserId).getNickname());
  819 + printDataMap.put("processNum", printData.getProcessInstanceId());
  820 + printDataMap.put("printTime", DatePattern.NORM_DATETIME_MINUTE_FORMAT.format(new DateTime()));
  821 + printDataMap.put("startUser", printData.getStartUser());
  822 + printDataMap.put("startTime", printData.getStartTime());
  823 + printDataMap.put("startUserDept", printData.getStartUserDept());
  824 + printDataMap.put("processStatus", printData.getProcessStatusShow());
  825 + // 自定义模板
  826 + if (printData.getPrintTemplateEnable() && printTemplateSetting != null) {
  827 + Document document = Jsoup.parse(printTemplateSetting.getTemplate());
  828 + // 添加table的border
  829 + Elements tables = document.select("table");
  830 + tables.forEach(item -> {
  831 + item.attr("border", "1");
  832 + });
  833 + // 替换所有mention
  834 + Elements mention = document.getElementsByAttributeValue("data-w-e-type", "mention");
  835 + mention.forEach(item -> {
  836 + String mentionId = JSONUtil.parseObj(URLUtil.decode(item.attr("data-info"))).getStr("id");
  837 + item.html(printDataMap.get(mentionId));
  838 + });
  839 + // 替换流程记录
  840 + Elements processRecords = document.getElementsByAttributeValue("data-w-e-type", "process-record");
  841 + Element processRecordElement = null;
  842 + if (!processRecords.isEmpty()) {
  843 + processRecordElement = new Element("table", "")
  844 + .attr("style", "width:100%;")
  845 + .attr("border", "1");
  846 + Element tbody = new Element("tbody", "");
  847 + Element rowHead = new Element("tr", "");
  848 + rowHead.appendChild(new Element("td", "")
  849 + .attr("colspan", "2")
  850 + .attr("width", "auto")
  851 + .attr("style", "text-align: center;")
  852 + .html("流程节点"));
  853 + tbody.appendChild(rowHead);
  854 + for (BpmProcessPrintDataRespVO.ApproveNode item : printData.getApproveNodes()) {
  855 + Element row = new Element("tr", "");
  856 + row.appendChild(new Element("td", "")
  857 + .html(item.getNodeName()));
  858 + row.appendChild(new Element("td", "")
  859 + .html(item.getNodeDesc()));
  860 + tbody.appendChild(row);
  861 + }
  862 + processRecordElement.appendChild(tbody);
  863 + }
  864 + for (Element item : processRecords) {
  865 + item.html(processRecordElement.outerHtml());
  866 + }
  867 + printData.setPrintTemplateHtml(document.html());
  868 + }
  869 + return printData;
  870 + }
  871 +
729 872 // ========== Update 写入相关方法 ==========
730 873  
731 874 @Override
... ...