diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java index d611fdf23..cf2af8f2a 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.collection; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import com.google.common.collect.ImmutableMap; import java.util.*; @@ -73,6 +74,13 @@ public class CollectionUtils { return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList()); } + public static PageResult convertPage(PageResult from, Function func) { + if (ArrayUtil.isEmpty(from)) { + return new PageResult<>(from.getTotal()); + } + return new PageResult<>(convertList(from.getList(), func), from.getTotal()); + } + public static List convertListByFlatMap(Collection from, Function> func) { if (CollUtil.isEmpty(from)) { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java index 8b97d6ae5..e8e2f9c71 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils; 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.task.vo.cc.BpmProcessInstanceCopyRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; @@ -28,8 +29,7 @@ import java.util.Map; import java.util.stream.Stream; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Tag(name = "管理后台 - 流程实例抄送") @@ -62,11 +62,15 @@ public class BpmProcessInstanceCopyController { convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId)); Map userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator())))); - return success(BeanUtils.toBean(pageResult, BpmProcessInstanceCopyRespVO.class, copyVO -> { - MapUtils.findAndThen(userMap, Long.valueOf(copyVO.getCreator()), user -> copyVO.setCreatorName(user.getNickname())); - MapUtils.findAndThen(userMap, copyVO.getStartUserId(), user -> copyVO.setStartUserName(user.getNickname())); + return success(convertPage(pageResult, copy -> { + BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class); + MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()), + user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); + MapUtils.findAndThen(userMap, copy.getStartUserId(), + user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(), processInstance -> copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime()))); + return copyVO; })); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java index fb925c08d..b087c3a44 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc; +import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -12,32 +13,30 @@ public class BpmProcessInstanceCopyRespVO { @Schema(description = "抄送主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "发起人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") - private Long startUserId; - @Schema(description = "发起人昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String startUserName; + @Schema(description = "发起人", requiredMode = Schema.RequiredMode.REQUIRED) + private UserSimpleBaseVO startUser; @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233") private String processInstanceId; - @Schema(description = "流程实例的名称",requiredMode = Schema.RequiredMode.REQUIRED, example = "测试") + @Schema(description = "流程实例的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试") private String processInstanceName; - @Schema(description = "流程实例的发起时间",requiredMode = Schema.RequiredMode.REQUIRED) + @Schema(description = "流程实例的发起时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime processInstanceStartTime; - @Schema(description = "抄送的节点的活动编号") + @Schema(description = "流程活动的编号", requiredMode = Schema.RequiredMode.REQUIRED) private String activityId; - @Schema(description = "发起抄送的任务编号") - private String taskId; - @Schema(description = "发起抄送的任务名称") - private String taskName; + @Schema(description = "流程活动的名字", requiredMode = Schema.RequiredMode.REQUIRED) + private String activityName; + + @Schema(description = "流程活动的编号") + private String taskId; - @Schema(description = "抄送人", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String creator; - @Schema(description = "抄送人昵称") - private String creatorName; @Schema(description = "抄送人意见") private String reason; + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED) + private UserSimpleBaseVO createUser; + @Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java index dfeb79260..5bec02660 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -4,7 +4,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import lombok.Data; -import java.util.Collection; import java.util.Map; @Schema(description = "管理后台 - 通过流程任务的 Request VO") @@ -19,9 +18,6 @@ public class BpmTaskApproveReqVO { @NotEmpty(message = "审批意见不能为空") private String reason; - @Schema(description = "抄送的用户编号数组", example = "1,2") - private Collection copyUserIds; - @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) private Map variables; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java index 9fc706177..fff8e0aec 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java @@ -4,6 +4,8 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; +import org.flowable.bpmn.model.FlowNode; +import org.flowable.task.api.history.HistoricTaskInstance; /** * 流程抄送 DO @@ -49,23 +51,25 @@ public class BpmProcessInstanceCopyDO extends BaseDO { */ private String category; /** - * 流程活动编号 + * 流程活动的编号 *

- * 对应 BPMN XML 节点编号,用于查询抄送节点的表单字段权限 - * 这里冗余的原因:如果是钉钉易搭的抄送节点 (ServiceTask),使用 taskId 可能查不到对应的 activityId + * + * 冗余 {@link FlowNode#getId()},对应 BPMN XML 节点编号 + * 原因:用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask),没有 taskId,只有 activityId */ private String activityId; /** - * 任务主键 - * 关联 Task 的 id 属性 + * 流程活动的名字 + * + * 冗余 {@link FlowNode#getName()} + */ + private String activityName; + /** + * 流程活动的编号 + * + * 关联 {@link HistoricTaskInstance#getId()} */ private String taskId; - /** - * 任务名称 - * - * 冗余 Task 的 name 属性 - */ - private String taskName; /** * 用户编号(被抄送的用户编号) diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java index bc2da56ff..1546f46be 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java @@ -16,7 +16,7 @@ import static cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCo /** * 处理抄送用户的 {@link JavaDelegate} 的实现类 *

- * 目前只有快搭模式的【抄送节点】使用 + * 目前只有仿钉钉/飞书模式的【抄送节点】使用 * * @author jason */ @@ -41,7 +41,7 @@ public class BpmCopyTaskDelegate implements JavaDelegate { // 2. 执行抄送 FlowElement currentFlowElement = execution.getCurrentFlowElement(); processInstanceCopyService.createProcessInstanceCopy(userIds, null, execution.getProcessInstanceId(), - currentFlowElement.getId(), null, currentFlowElement.getName()); + currentFlowElement.getId(), currentFlowElement.getName(), null); } } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java index 10a74c977..3cbac0616 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyService.java @@ -3,9 +3,10 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import jakarta.validation.constraints.NotEmpty; +import org.flowable.bpmn.model.FlowNode; import java.util.Collection; -import java.util.Set; /** * 流程抄送 Service 接口 @@ -15,7 +16,7 @@ import java.util.Set; public interface BpmProcessInstanceCopyService { /** - * 流程实例的抄送 + * 【管理员】流程实例的抄送 * * @param userIds 抄送的用户编号 * @param reason 抄送意见 @@ -24,17 +25,20 @@ public interface BpmProcessInstanceCopyService { void createProcessInstanceCopy(Collection userIds, String reason, String taskId); /** - * 流程实例的抄送 + * 【自动抄送】流程实例的抄送 * * @param userIds 抄送的用户编号 * @param reason 抄送意见 * @param processInstanceId 流程编号 - * @param activityId 流程活动编号 id (对应 BPMN XML 节点 Id) - * @param taskId 任务编号 - * @param taskName 任务名称 + * @param activityId 流程活动编号(对应 {@link FlowNode#getId()}) + * @param activityName 任务编号(对应 {@link FlowNode#getName()}) + * @param taskId 任务编号,允许空 */ - void createProcessInstanceCopy(Collection userIds, String reason, String processInstanceId, String activityId, - String taskId, String taskName); + void createProcessInstanceCopy(Collection userIds, String reason, + @NotEmpty(message = "流程实例编号不能为空") String processInstanceId, + @NotEmpty(message = "流程活动编号不能为空") String activityId, + @NotEmpty(message = "流程活动名字不能为空") String activityName, + String taskId); /** * 获得抄送的流程的分页 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java index 538999257..c2d52de05 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.util.ObjectUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper; @@ -19,7 +18,6 @@ import org.springframework.validation.annotation.Validated; import java.util.Collection; import java.util.List; -import java.util.Set; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; @@ -54,13 +52,14 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy if (ObjectUtil.isNull(task)) { throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); } - String processInstanceId = task.getProcessInstanceId(); - createProcessInstanceCopy(userIds, reason, processInstanceId, task.getTaskDefinitionKey(), task.getId(), task.getName()); + // 执行抄送 + createProcessInstanceCopy(userIds, reason, + task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getId(), task.getName()); } @Override - public void createProcessInstanceCopy(Collection userIds, String reason, String processInstanceId, String activityId, - String taskId, String taskName) { + public void createProcessInstanceCopy(Collection userIds, String reason, String processInstanceId, + String activityId, String activityName, String taskId) { // 1.1 校验流程实例存在 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); if (processInstance == null) { @@ -77,8 +76,8 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy List copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO() .setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId())) .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) - .setCategory(processDefinition.getCategory()).setActivityId(activityId) - .setTaskId(taskId).setTaskName(taskName)); + .setCategory(processDefinition.getCategory()).setTaskId(taskId) + .setActivityId(activityId).setActivityName(activityName)); processInstanceCopyMapper.insertBatch(copyList); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 70626a03e..1ca8db8f7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -426,11 +426,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { throw exception(PROCESS_INSTANCE_NOT_EXISTS); } - // 2. 抄送用户 - if (CollUtil.isNotEmpty(reqVO.getCopyUserIds())) { - processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), null, reqVO.getId()); - } - // 情况一:被委派的任务,不调用 complete 去完成任务 if (DelegationState.PENDING.equals(task.getDelegationState())) { approveDelegateTask(reqVO, task); @@ -444,12 +439,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { } // 情况三:审批普通的任务。大多数情况下,都是这样 - // 3.1 更新 task 状态、原因 + // 2.1 更新 task 状态、原因 updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason()); - // 3.2 添加评论 + // 2.2 添加评论 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); - // 3.3 调用 BPM complete 去完成任务 + // 2.3 调用 BPM complete 去完成任务 // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 if (CollUtil.isNotEmpty(reqVO.getVariables())) { Map variables = FlowableUtils.filterTaskFormVariable(reqVO.getVariables());