diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java index 654bf080..9317c487 100644 --- a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java @@ -1,6 +1,5 @@ package org.dromara.workflow.domain.bo; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import org.dromara.common.core.validate.AddGroup; @@ -12,7 +11,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; - /** * 驳回参数请求 * @@ -50,6 +48,11 @@ public class BackProcessBo implements Serializable { */ private String message; + /** + * 手写签名(Base64编码的图片数据) + */ + private String signature; + /** * 通知 */ diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java index e5befff7..7af84053 100644 --- a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java @@ -25,7 +25,7 @@ public class CompleteTaskBo implements Serializable { /** * 任务id */ - @NotNull(message = "任务id不能为空", groups = {AddGroup.class}) + @NotNull(message = "任务id不能为空", groups = { AddGroup.class }) private Long taskId; /** @@ -48,6 +48,11 @@ public class CompleteTaskBo implements Serializable { */ private String message; + /** + * 手写签名(Base64编码的图片数据) + */ + private String signature; + /** * 消息通知 */ diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java index 8776a76b..026405ca 100644 --- a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/FlowHisTaskVo.java @@ -151,6 +151,11 @@ public class FlowHisTaskVo implements Serializable { */ private String message; + /** + * 手写签名(Base64编码的图片数据) + */ + private String signature; + /** * 业务详情 存业务类的json */ diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwHisTaskExtMapper.java b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwHisTaskExtMapper.java new file mode 100644 index 00000000..36bbcee0 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwHisTaskExtMapper.java @@ -0,0 +1,48 @@ +package org.dromara.workflow.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; +import org.dromara.workflow.domain.vo.FlowHisTaskVo; + +import java.util.List; + +/** + * 历史任务扩展Mapper + * 用于处理Warm-Flow框架不支持的自定义字段 + * + * @author System + */ +@Mapper +public interface FlwHisTaskExtMapper { + + /** + * 更新历史任务的签名字段 + * + * @param taskId 任务ID + * @param signature 签名数据(Base64) + * @return 影响行数 + */ + @Update("UPDATE flow_his_task SET signature = #{signature} WHERE task_id = #{taskId}") + int updateSignatureByTaskId(@Param("taskId") Long taskId, @Param("signature") String signature); + + /** + * 根据实例ID更新签名字段(批量更新场景) + * + * @param instanceId 流程实例ID + * @param signature 签名数据(Base64) + * @return 影响行数 + */ + @Update("UPDATE flow_his_task SET signature = #{signature} WHERE instance_id = #{instanceId}") + int updateSignatureByInstanceId(@Param("instanceId") Long instanceId, @Param("signature") String signature); + + /** + * 根据实例ID查询历史任务列表(包含签名字段) + * + * @param instanceId 流程实例ID + * @param nodeType 节点类型 + * @return 历史任务列表 + */ + List selectHisTaskListWithSignature(@Param("instanceId") Long instanceId, + @Param("nodeType") Integer nodeType); +} diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 848bec4e..590f8f4c 100644 --- a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -42,6 +42,7 @@ import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowInstanceVo; import org.dromara.workflow.handler.FlowProcessEventHandler; import org.dromara.workflow.mapper.FlwCategoryMapper; +import org.dromara.workflow.mapper.FlwHisTaskExtMapper; import org.dromara.workflow.mapper.FlwInstanceMapper; import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwTaskService; @@ -71,6 +72,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { private final IFlwTaskService flwTaskService; private final FlwInstanceMapper flwInstanceMapper; private final FlwCategoryMapper flwCategoryMapper; + private final FlwHisTaskExtMapper flwHisTaskExtMapper; /** * 分页查询正在运行的流程实例 @@ -128,15 +130,21 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { */ private QueryWrapper buildQueryWrapper(FlowInstanceBo flowInstanceBo) { QueryWrapper queryWrapper = Wrappers.query(); - queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getNodeName()), "fi.node_name", flowInstanceBo.getNodeName()); - queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowName()), "fd.flow_name", flowInstanceBo.getFlowName()); - queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowCode()), "fd.flow_code", flowInstanceBo.getFlowCode()); + queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getNodeName()), "fi.node_name", + flowInstanceBo.getNodeName()); + queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowName()), "fd.flow_name", + flowInstanceBo.getFlowName()); + queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowCode()), "fd.flow_code", + flowInstanceBo.getFlowCode()); if (StringUtils.isNotBlank(flowInstanceBo.getCategory())) { - List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowInstanceBo.getCategory())); + List categoryIds = flwCategoryMapper + .selectCategoryIdsByParentId(Convert.toLong(flowInstanceBo.getCategory())); queryWrapper.in("fd.category", StreamUtils.toList(categoryIds, Convert::toStr)); } - queryWrapper.eq(StringUtils.isNotBlank(flowInstanceBo.getBusinessId()), "fi.business_id", flowInstanceBo.getBusinessId()); - queryWrapper.in(CollUtil.isNotEmpty(flowInstanceBo.getCreateByIds()), "fi.create_by", flowInstanceBo.getCreateByIds()); + queryWrapper.eq(StringUtils.isNotBlank(flowInstanceBo.getBusinessId()), "fi.business_id", + flowInstanceBo.getBusinessId()); + queryWrapper.in(CollUtil.isNotEmpty(flowInstanceBo.getCreateByIds()), "fi.create_by", + flowInstanceBo.getCreateByIds()); queryWrapper.eq("fi.del_flag", "0"); queryWrapper.orderByDesc("fi.create_time"); return queryWrapper; @@ -149,7 +157,8 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { */ @Override public FlowInstance selectInstByBusinessId(String businessId) { - return flowInstanceMapper.selectOne(new LambdaQueryWrapper().eq(FlowInstance::getBusinessId, businessId)); + return flowInstanceMapper + .selectOne(new LambdaQueryWrapper().eq(FlowInstance::getBusinessId, businessId)); } /** @@ -180,7 +189,8 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { @Override @Transactional(rollbackFor = Exception.class) public boolean deleteByBusinessIds(List businessIds) { - List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds, Convert::toStr))); + List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper() + .in(FlowInstance::getBusinessId, StreamUtils.toList(businessIds, Convert::toStr))); if (CollUtil.isEmpty(flowInstances)) { log.warn("未找到对应的流程实例信息,无法执行删除操作。"); return false; @@ -204,8 +214,8 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { } // 获取定义信息 Map definitionMap = defService.getByIds( - StreamUtils.toList(instances, Instance::getDefinitionId) - ).stream().collect(Collectors.toMap(Definition::getId, definition -> definition)); + StreamUtils.toList(instances, Instance::getDefinitionId)).stream() + .collect(Collectors.toMap(Definition::getId, definition -> definition)); // 逐一触发删除事件 instances.forEach(instance -> { @@ -242,11 +252,11 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { String userIdStr = LoginHelper.getUserIdStr(); BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus()); FlowParams flowParams = FlowParams.build() - .message(message) - .flowStatus(BusinessStatusEnum.CANCEL.getStatus()) - .hisStatus(BusinessStatusEnum.CANCEL.getStatus()) - .handler(userIdStr) - .ignore(true); + .message(message) + .flowStatus(BusinessStatusEnum.CANCEL.getStatus()) + .hisStatus(BusinessStatusEnum.CANCEL.getStatus()) + .handler(userIdStr) + .ignore(true); taskService.revoke(instance.getId(), flowParams); } catch (Exception e) { log.error("撤销失败: {}", e.getMessage(), e); @@ -281,7 +291,7 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); } Long instanceId = flowInstance.getId(); - //运行中的任务 + // 运行中的任务 List list = new ArrayList<>(); List flowTaskList = flwTaskService.selectByInstId(instanceId); if (CollUtil.isNotEmpty(flowTaskList)) { @@ -302,16 +312,13 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { } list.addAll(flowHisTaskVos); } - //历史任务 - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(FlowHisTask::getInstanceId, instanceId); - wrapper.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()); - wrapper.orderByDesc(FlowHisTask::getCreateTime).orderByDesc(FlowHisTask::getUpdateTime); - List flowHisTasks = flowHisTaskMapper.selectList(wrapper); - if (CollUtil.isNotEmpty(flowHisTasks)) { - list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class)); + // 历史任务(使用自定义Mapper查询,包含签名字段) + List flowHisTaskVos = flwHisTaskExtMapper.selectHisTaskListWithSignature(instanceId, + NodeType.BETWEEN.getKey()); + if (CollUtil.isNotEmpty(flowHisTaskVos)) { + list.addAll(flowHisTaskVos); } - return Map.of("list", list,"instanceId",instanceId); + return Map.of("list", list, "instanceId", instanceId); } /** @@ -336,10 +343,11 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { @Override public Map instanceVariable(Long instanceId) { FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId); - Map variableMap = Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap()); + Map variableMap = Optional.ofNullable(flowInstance.getVariableMap()) + .orElse(Collections.emptyMap()); List> variableList = variableMap.entrySet().stream() - .map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue())) - .toList(); + .map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue())) + .toList(); return Map.of("variableList", variableList, "variable", flowInstance.getVariable()); } @@ -416,10 +424,10 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); } FlowParams flowParams = FlowParams.build() - .message(bo.getComment()) - .flowStatus(BusinessStatusEnum.INVALID.getStatus()) - .hisStatus(TaskStatusEnum.INVALID.getStatus()) - .ignore(true); + .message(bo.getComment()) + .flowStatus(BusinessStatusEnum.INVALID.getStatus()) + .hisStatus(TaskStatusEnum.INVALID.getStatus()) + .ignore(true); taskService.terminationByInsId(bo.getId(), flowParams); return true; } catch (Exception e) { diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 8fc0340e..c6c90803 100644 --- a/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -38,6 +38,7 @@ import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper; import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.mapper.FlwHisTaskExtMapper; import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskAssigneeType; import org.dromara.workflow.common.enums.TaskStatusEnum; @@ -86,6 +87,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { private final IFlwTaskAssigneeService flwTaskAssigneeService; private final IFlwCommonService flwCommonService; private final IFlwNodeExtService flwNodeExtService; + private final FlwHisTaskExtMapper flwHisTaskExtMapper; /** * 启动任务 @@ -185,6 +187,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService { .hisTaskExt(completeTaskBo.getFileId()); // 执行任务跳转,并根据返回的处理人设置下一步处理人 taskService.skip(taskId, flowParams); + + // 保存手写签名到历史任务表 + if (StringUtils.isNotBlank(completeTaskBo.getSignature())) { + flwHisTaskExtMapper.updateSignatureByTaskId(taskId, completeTaskBo.getSignature()); + } + return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -409,6 +417,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService { .hisStatus(TaskStatusEnum.BACK.getStatus()) .hisTaskExt(bo.getFileId()); taskService.skip(task.getId(), flowParams); + + // 保存手写签名到历史任务表(驳回场景) + if (StringUtils.isNotBlank(bo.getSignature())) { + flwHisTaskExtMapper.updateSignatureByTaskId(taskId, bo.getSignature()); + } + return true; } catch (Exception e) { log.error(e.getMessage(), e); diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwHisTaskExtMapper.xml b/backend/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwHisTaskExtMapper.xml new file mode 100644 index 00000000..e55dd78d --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwHisTaskExtMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml b/backend/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml index f539030b..6d4aeeac 100644 --- a/backend/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml +++ b/backend/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/FlwTaskMapper.xml @@ -59,6 +59,7 @@ a.instance_id, a.flow_status flow_task_status, a.message, + a.signature, a.ext, a.create_time, a.update_time, diff --git a/web/src/components/Process/approvalRecord.vue b/web/src/components/Process/approvalRecord.vue index b13c426d..0bc459a3 100644 --- a/web/src/components/Process/approvalRecord.vue +++ b/web/src/components/Process/approvalRecord.vue @@ -33,6 +33,21 @@ + + + + +