diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
index 943a82d546..5284281f10 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
@@ -100,6 +100,10 @@ public class BpmModelMetaInfoVO {
@Schema(description = "任务后置通知设置", example = "{}")
private HttpRequestSetting taskAfterTriggerSetting;
+ @Schema(description = "自定义打印模板设置", example = "{}")
+ @Valid
+ private PrintTemplateSetting printTemplateSetting;
+
@Schema(description = "流程 ID 规则")
@Data
@Valid
@@ -180,4 +184,17 @@ public class BpmModelMetaInfoVO {
}
+ @Schema(description = "自定义打印模板设置")
+ @Data
+ public static class PrintTemplateSetting {
+
+ @Schema(description = "是否自定义打印模板", example = "false")
+ @NotNull(message = "是否自定义打印模板不能为空")
+ private Boolean enable;
+
+ @Schema(description = "打印模板", example = "
")
+ private String template;
+
+ }
+
}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
index 8a3777772e..b66555cda3 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
@@ -196,4 +196,12 @@ public class BpmProcessInstanceController {
return success(processInstanceService.getProcessInstanceBpmnModelView(id));
}
+ @GetMapping("/get-print-data")
+ @Operation(summary = "获得打印数据")
+ @Parameter(name = "id", description = "流程实例的编号", required = true)
+ @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
+ public CommonResult getPrintData(@RequestParam("processInstanceId") String processInstanceId) {
+ return success(processInstanceService.getPrintData(getLoginUserId(), processInstanceId));
+ }
+
}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java
new file mode 100644
index 0000000000..a7897f1845
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java
@@ -0,0 +1,65 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+
+@Schema(description = "管理后台 - 打印数据 Response VO")
+@Data
+public class BpmProcessPrintDataRespVO {
+
+ private Boolean printTemplateEnable;
+
+ private Integer processStatus;
+
+ private String processStatusShow;
+
+ private String processInstanceId;
+
+ private String processBusinessKey;
+
+ private String processName;
+
+ private String startUser;
+
+ private String startUserDept;
+
+ private String startTime;
+
+ private List approveNodes;
+
+ private List formFields;
+
+ private String printTemplateHtml;
+
+ @Data
+ public static class ApproveNode {
+
+ private String nodeName;
+
+ private String nodeDesc;
+
+ private String signUrl;
+
+ private String nodeId;
+
+ }
+
+ @Data
+ public static class FormField {
+
+ private String formId;
+
+ private String formName;
+
+ private String formType;
+
+ private String formValue;
+
+ private String formValueShow;
+
+ }
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
index 37e2c4462d..6fd905ee3d 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
@@ -224,4 +224,10 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting;
+ /**
+ * 自定义打印模板设置
+ */
+ @TableField(typeHandler = JacksonTypeHandler.class)
+ private BpmModelMetaInfoVO.PrintTemplateSetting printTemplateSetting;
+
}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java
index 5b42df50fe..87be32633b 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.task;
+import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
@@ -47,4 +48,8 @@ public enum BpmProcessInstanceStatusEnum implements ArrayValuable {
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());
}
+ public static BpmProcessInstanceStatusEnum valueOf(Integer status) {
+ return ArrayUtil.firstMatch(item -> item.getStatus().equals(status), values());
+ }
+
}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java
index 9ba3b5cb3a..0f69c42795 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.enums.task;
+import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
@@ -68,4 +69,8 @@ public enum BpmTaskStatusEnum {
return ObjUtil.equal(status, CANCEL.getStatus());
}
+ public static BpmTaskStatusEnum valueOf(Integer status) {
+ return ArrayUtil.firstMatch(item -> item.getStatus().equals(status), values());
+ }
+
}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
index abba2245e2..33e667716b 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
@@ -113,6 +113,14 @@ public interface BpmProcessInstanceService {
*/
BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id);
+ /**
+ * 获取流程打印所需数据
+ * @param loginUserId 打印人
+ * @param processInstanceId 流程实例id
+ * @return 打印所需数据
+ */
+ BpmProcessPrintDataRespVO getPrintData(Long loginUserId, String processInstanceId);
+
// ========== Update 写入相关方法 ==========
/**
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
index d26d406d6d..9c2c06525a 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
+++ b/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;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.*;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
@@ -57,6 +61,10 @@ import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -726,6 +734,141 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
userMap, deptMap);
}
+ @Override
+ public BpmProcessPrintDataRespVO getPrintData(Long loginUserId, String processInstanceId) {
+ // TODO 方法抽离
+ // 流程实例
+ HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(processInstanceId);
+ if (historicProcessInstance == null) {
+ throw exception(PROCESS_INSTANCE_NOT_EXISTS);
+ }
+ // 准备数据
+ BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService
+ .getProcessDefinitionInfo(historicProcessInstance.getProcessDefinitionId());
+ BpmModelMetaInfoVO.PrintTemplateSetting printTemplateSetting = processDefinitionInfo.getPrintTemplateSetting();
+ List formFieldList = processDefinitionInfo.getFormFields();
+ List formFieldObjList = formFieldList.stream().map(JSONUtil::parseObj).toList();
+ List tasks = historyService.createHistoricTaskInstanceQuery()
+ .finished()
+ .includeTaskLocalVariables()
+ .processInstanceId(processInstanceId)
+ .taskVariableValueNotEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS,
+ BpmTaskStatusEnum.CANCEL.getStatus())
+ .orderByHistoricTaskInstanceStartTime().asc().list();
+ Set userIds = convertSet(tasks, item -> Long.valueOf(item.getAssignee()));
+ userIds.add(loginUserId);
+ Map userMap = adminUserApi.getUserMap(userIds);
+ HashMap printDataMap = new HashMap<>(8 + formFieldList.size());
+ // 返回打印所需数据
+ BpmProcessPrintDataRespVO printData = new BpmProcessPrintDataRespVO();
+ // 打印模板是否开启
+ printData.setPrintTemplateEnable(printTemplateSetting != null && Boolean.TRUE.equals(printTemplateSetting.getEnable()));
+ // 流程相关数据
+ printData.setProcessStatus(FlowableUtils.getProcessInstanceStatus(historicProcessInstance));
+ printData.setProcessStatusShow(BpmProcessInstanceStatusEnum.valueOf(printData.getProcessStatus()).getDesc());
+ printData.setProcessInstanceId(historicProcessInstance.getId());
+ printData.setProcessName(historicProcessInstance.getName());
+ printData.setProcessBusinessKey(historicProcessInstance.getBusinessKey());
+ printData.setStartTime(DatePattern.NORM_DATETIME_MINUTE_FORMAT.format(historicProcessInstance.getStartTime()));
+ // 发起人
+ AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(historicProcessInstance.getStartUserId()));
+ DeptRespDTO dept = deptApi.getDept(startUser.getDeptId());
+ printData.setStartUser(startUser.getNickname());
+ printData.setStartUserDept(dept.getName());
+ // 审批历史
+ List approveNodes = new ArrayList<>(tasks.size());
+ tasks.forEach(item -> {
+ Map taskLocalVariables = item.getTaskLocalVariables();
+ BpmProcessPrintDataRespVO.ApproveNode approveNode = new BpmProcessPrintDataRespVO.ApproveNode();
+ approveNode.setNodeName(item.getName());
+ approveNode.setNodeId(item.getId());
+ approveNode.setSignUrl((String) taskLocalVariables.getOrDefault(BpmnVariableConstants.TASK_SIGN_PIC_URL, ""));
+ approveNode.setNodeDesc(StrUtil.format("{} / {} / {} / {} / {}",
+ userMap.get(Long.valueOf(item.getAssignee())).getNickname(),
+ item.getName(),
+ DateUtil.formatDateTime(item.getEndTime()),
+ BpmTaskStatusEnum.valueOf((Integer) taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_STATUS)).getName(),
+ taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_REASON)));
+ approveNodes.add(approveNode);
+ });
+ printData.setApproveNodes(approveNodes);
+ // 表单数据
+ Map processVariables = historicProcessInstance.getProcessVariables();
+ List formFields = new ArrayList<>(formFieldList.size());
+ formFieldObjList.forEach(item -> {
+ BpmProcessPrintDataRespVO.FormField formField = new BpmProcessPrintDataRespVO.FormField();
+ formField.setFormName(item.getStr("title"));
+ formField.setFormType(item.getStr("type"));
+ formField.setFormId(item.getStr("field"));
+ formField.setFormValue(processVariables.getOrDefault(item.getStr("field"), "").toString());
+ // TODO 根据不同类型的表单展示,下面为图片和输入框示例
+ if (formField.getFormType().equals("input")) {
+ formField.setFormValueShow(processVariables.getOrDefault(item.getStr("field"), "").toString());
+ } else if (formField.getFormType().equals("UploadImg")) {
+ formField.setFormValueShow("
");
+ } else {
+ formField.setFormValueShow("此类型表单展示未完善");
+ }
+ printDataMap.put(formField.getFormId(), formField.getFormValueShow());
+ formFields.add(formField);
+ });
+ printData.setFormFields(formFields);
+ // 数据映射
+ printDataMap.put("processName", printData.getProcessName());
+ printDataMap.put("printUsername", userMap.get(loginUserId).getNickname());
+ printDataMap.put("processNum", printData.getProcessInstanceId());
+ printDataMap.put("printTime", DatePattern.NORM_DATETIME_MINUTE_FORMAT.format(new DateTime()));
+ printDataMap.put("startUser", printData.getStartUser());
+ printDataMap.put("startTime", printData.getStartTime());
+ printDataMap.put("startUserDept", printData.getStartUserDept());
+ printDataMap.put("processStatus", printData.getProcessStatusShow());
+ // 自定义模板
+ if (printData.getPrintTemplateEnable() && printTemplateSetting != null) {
+ Document document = Jsoup.parse(printTemplateSetting.getTemplate());
+ // 添加table的border
+ Elements tables = document.select("table");
+ tables.forEach(item -> {
+ item.attr("border", "1");
+ });
+ // 替换所有mention
+ Elements mention = document.getElementsByAttributeValue("data-w-e-type", "mention");
+ mention.forEach(item -> {
+ String mentionId = JSONUtil.parseObj(URLUtil.decode(item.attr("data-info"))).getStr("id");
+ item.html(printDataMap.get(mentionId));
+ });
+ // 替换流程记录
+ Elements processRecords = document.getElementsByAttributeValue("data-w-e-type", "process-record");
+ Element processRecordElement = null;
+ if (!processRecords.isEmpty()) {
+ processRecordElement = new Element("table", "")
+ .attr("style", "width:100%;")
+ .attr("border", "1");
+ Element tbody = new Element("tbody", "");
+ Element rowHead = new Element("tr", "");
+ rowHead.appendChild(new Element("td", "")
+ .attr("colspan", "2")
+ .attr("width", "auto")
+ .attr("style", "text-align: center;")
+ .html("流程节点"));
+ tbody.appendChild(rowHead);
+ for (BpmProcessPrintDataRespVO.ApproveNode item : printData.getApproveNodes()) {
+ Element row = new Element("tr", "");
+ row.appendChild(new Element("td", "")
+ .html(item.getNodeName()));
+ row.appendChild(new Element("td", "")
+ .html(item.getNodeDesc()));
+ tbody.appendChild(row);
+ }
+ processRecordElement.appendChild(tbody);
+ }
+ for (Element item : processRecords) {
+ item.html(processRecordElement.outerHtml());
+ }
+ printData.setPrintTemplateHtml(document.html());
+ }
+ return printData;
+ }
+
// ========== Update 写入相关方法 ==========
@Override