Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
jason
2025-09-27 20:59:06 +08:00
39 changed files with 929 additions and 858 deletions

View File

@@ -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 = "<p></p>")
private String template;
}
}

View File

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
@@ -26,6 +27,7 @@ import jakarta.validation.Valid;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -34,9 +36,11 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS;
@Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请”
@RestController
@@ -192,8 +196,30 @@ public class BpmProcessInstanceController {
@GetMapping("/get-bpmn-model-view")
@Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用")
@Parameter(name = "id", description = "流程实例的编号", required = true)
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) {
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(
@RequestParam(value = "id") String id) {
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<BpmProcessPrintDataRespVO> getProcessInstancePrintData(
@RequestParam("processInstanceId") String processInstanceId) {
HistoricProcessInstance historicProcessInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
if (historicProcessInstance == null) {
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
}
AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(historicProcessInstance.getStartUserId()));
DeptRespDTO dept = deptApi.getDept(startUser.getDeptId());
List<HistoricTaskInstance> tasks = taskService.getFinishedTaskListByProcessInstanceIdWithoutCancel(processInstanceId);
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(tasks, item -> Long.valueOf(item.getAssignee())));
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePrintData(historicProcessInstance,
processDefinitionService.getProcessDefinitionInfo(historicProcessInstance.getProcessDefinitionId()),
tasks, userMap,
new UserSimpleBaseVO().setNickname(startUser.getNickname()).setDeptName(dept.getName())));
}
}

View File

@@ -0,0 +1,42 @@
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 {
@Schema(description = "流程实例数据")
private BpmProcessInstanceRespVO processInstance;
@Schema(description = "是否开启自定义打印模板", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean printTemplateEnable;
@Schema(description = "自定义打印模板 HTML")
private String printTemplateHtml;
@Schema(description = "审批任务列表")
private List<Task> tasks;
@Schema(description = "流程任务")
@Data
public static class Task {
@Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "签名 URL", example = "https://www.iocoder.cn/sign.png")
private String signPicUrl;
@Schema(description = "任务描述", requiredMode = Schema.RequiredMode.REQUIRED)
private String description; // 该字段由后端拼接
}
}

View File

@@ -1,23 +1,29 @@
package cn.iocoder.yudao.module.bpm.convert.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessPrintDataRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
@@ -35,10 +41,7 @@ import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -292,4 +295,47 @@ public interface BpmProcessInstanceConvert {
.setActivityNodes(activityNodes);
}
default BpmProcessPrintDataRespVO buildProcessInstancePrintData(HistoricProcessInstance historicProcessInstance,
BpmProcessDefinitionInfoDO processDefinitionInfo,
List<HistoricTaskInstance> tasks,
Map<Long, AdminUserRespDTO> userMap,
UserSimpleBaseVO startUser) {
BpmModelMetaInfoVO.PrintTemplateSetting printTemplateSetting = processDefinitionInfo.getPrintTemplateSetting();
BpmProcessPrintDataRespVO printData = new BpmProcessPrintDataRespVO();
// 打印模板是否开启
printData.setPrintTemplateEnable(printTemplateSetting != null && Boolean.TRUE.equals(printTemplateSetting.getEnable()));
// 流程相关数据
BpmProcessInstanceRespVO processInstance = new BpmProcessInstanceRespVO()
.setId(historicProcessInstance.getId()).setName(historicProcessInstance.getName())
.setBusinessKey(historicProcessInstance.getBusinessKey())
.setStartTime(DateUtils.of(historicProcessInstance.getStartTime()))
.setEndTime(DateUtils.of(historicProcessInstance.getEndTime()))
.setStartUser(startUser).setStatus(FlowableUtils.getProcessInstanceStatus(historicProcessInstance))
.setFormVariables(historicProcessInstance.getProcessVariables())
.setProcessDefinition(BeanUtils.toBean(processDefinitionInfo, BpmProcessDefinitionRespVO.class));
printData.setProcessInstance(processInstance);
// 审批历史
List<BpmProcessPrintDataRespVO.Task> approveTasks = new ArrayList<>(tasks.size());
tasks.forEach(item -> {
Map<String, Object> taskLocalVariables = item.getTaskLocalVariables();
BpmProcessPrintDataRespVO.Task approveTask = new BpmProcessPrintDataRespVO.Task();
approveTask.setName(item.getName());
approveTask.setId(item.getId());
approveTask.setSignPicUrl((String) taskLocalVariables.get(BpmnVariableConstants.TASK_SIGN_PIC_URL));
approveTask.setDescription(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)));
approveTasks.add(approveTask);
});
printData.setTasks(approveTasks);
// 自定义模板
if (printData.getPrintTemplateEnable() && printTemplateSetting != null) {
printData.setPrintTemplateHtml(printTemplateSetting.getTemplate());
}
return printData;
}
}

View File

@@ -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;
}

View File

@@ -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<Integer> {
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus());
}
public static BpmProcessInstanceStatusEnum valueOf(Integer status) {
return ArrayUtil.firstMatch(item -> item.getStatus().equals(status), values());
}
}

View File

@@ -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());
}
}

View File

@@ -2,6 +2,7 @@ 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.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
@@ -13,6 +14,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
@@ -974,7 +976,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
status);
}
// 1.3 如果子流程拒绝, 设置其父流程也为拒绝状态且结束父流程
// 1.3 如果子流程拒绝,设置其父流程也为拒绝状态且结束父流程
// 相关问题链接https://t.zsxq.com/kZhyb
if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())
&& StrUtil.isNotBlank(instance.getSuperExecutionId())) {
// 1.3.1 获取父流程实例 并标记为不通过
@@ -984,6 +987,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
// 1.3.2 结束父流程。需要在子流程结束事务提交后执行
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCompletion(int transactionStatus) {
// 回滚情况,直接返回
@@ -1043,7 +1047,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name);
}
// 流程前置通知:需要在流程启动后(事务提交后)variables 已设置。或者放在 PROCESS_STARTED 事件中处理,先放这里。
// 流程前置通知:需要在流程启动后(事务提交后),保证 variables 已设置
// 相关问题链接https://t.zsxq.com/DF7Kq
if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
return;
}

View File

@@ -176,6 +176,14 @@ public interface BpmTaskService {
*/
List<HistoricActivityInstance> getHistoricActivityListByExecutionId(String executionId);
/**
* 获得指定流程实例的已完成的流程任务列表,不包含取消状态
*
* @param processInstanceId 流程实例的编号
* @return 流程任务列表
*/
List<HistoricTaskInstance> getFinishedTaskListByProcessInstanceIdWithoutCancel(String processInstanceId);
// ========== Update 写入相关方法 ==========
/**

View File

@@ -492,6 +492,17 @@ public class BpmTaskServiceImpl implements BpmTaskService {
return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list();
}
@Override
public List<HistoricTaskInstance> getFinishedTaskListByProcessInstanceIdWithoutCancel(String processInstanceId) {
return historyService.createHistoricTaskInstanceQuery()
.finished()
.includeTaskLocalVariables()
.processInstanceId(processInstanceId)
.taskVariableValueNotEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS,
BpmTaskStatusEnum.CANCEL.getStatus())
.orderByHistoricTaskInstanceStartTime().asc().list();
}
/**
* 判断指定用户,是否是当前任务的审批人
*