删除 yudao-bpm 项目,进一步精简项目

This commit is contained in:
YunaiV 2024-03-27 21:19:25 +08:00
parent 7412900e83
commit 205d2ae4a9
66 changed files with 0 additions and 3266 deletions

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* BPM 流程监听器的类型
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmProcessListenerType {
EXECUTION("execution", "执行监听器"),
TASK("task", "任务执行器");
private final String type;
private final String name;
}

View File

@ -1,22 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.definition;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* BPM 流程监听器的值类型
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmProcessListenerValueType {
CLASS("class", "Java 类"),
DELEGATE_EXPRESSION("delegateExpression", "代理表达式"),
EXPRESSION("expression", "表达式");
private final String type;
private final String name;
}

View File

@ -1,45 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.task;
import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程实例/任务的删除原因枚举
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmDeleteReasonEnum {
// ========== 流程实例的独有原因 ==========
REJECT_TASK("审批不通过任务,原因:{}"), // 场景用户审批不通过任务修改文案时需要注意 isRejectReason 方法
CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景用户主动取消流程
CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景管理员取消流程
// ========== 流程任务的独有原因 ==========
CANCEL_BY_SYSTEM("系统自动取消"), // 场景非常多比如说1多任务审批已经满足条件无需审批该任务2流程实例被取消无需审批该任务等等
;
private final String reason;
/**
* 格式化理由
*
* @param args 参数
* @return 理由
*/
public String format(Object... args) {
return StrUtil.format(reason, args);
}
// ========== 逻辑 ==========
public static boolean isRejectReason(String reason) {
return StrUtil.startWith(reason, "审批不通过任务,原因:");
}
}

View File

@ -1,47 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.task;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程任务的加签类型枚举
*
* @author kehaiyou
*/
@Getter
@AllArgsConstructor
public enum BpmTaskSignTypeEnum {
/**
* 向前加签需要前置任务审批完成才回到原审批人
*/
BEFORE("before", "向前加签"),
/**
* 向后加签需要后置任务全部审批完才会通过原审批人节点
*/
AFTER("after", "向后加签");
/**
* 类型
*/
private final String type;
/**
* 名字
*/
private final String name;
public static String nameOfType(String type) {
for (BpmTaskSignTypeEnum value : values()) {
if (value.type.equals(type)) {
return value.name;
}
}
return null;
}
public static BpmTaskSignTypeEnum of(String type) {
return ArrayUtil.firstMatch(value -> value.getType().equals(type), values());
}
}

View File

@ -1,61 +0,0 @@
package cn.iocoder.yudao.module.bpm.enums.task;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程任务 Task 的状态枚举
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum BpmTaskStatusEnum {
RUNNING(1, "审批中"),
APPROVE(2, "审批通过"),
REJECT(3, "审批不通过"),
CANCEL(4, "已取消"),
RETURN(5, "已退回"),
DELEGATE(6, "委派中"),
/**
* 使用场景
* 1. 任务被向后加签它在审批通过后会变成 APPROVING 这个状态然后等到加签出来的任务都被审批后才会变成 APPROVE 审批通过
*/
APPROVING(7, "审批通过中"),
/**
* 使用场景
* 1. 任务被向前加签它会变成 WAIT 状态需要等待加签出来的任务被审批后它才能继续变为 RUNNING 继续审批
* 2. 任务被向后加签加签出来的任务处于 WAIT 状态它们需要等待该任务被审批后它们才能继续变为 RUNNING 继续审批
*/
WAIT(0, "待审批");
/**
* 状态
* <p>
* 如果新增时注意 {@link #isEndStatus(Integer)} 是否需要变更
*/
private final Integer status;
/**
* 名字
*/
private final String name;
/**
* 判断该状态是否已经处于 End 最终状态
* <p>
* 主要用于一些状态更新的逻辑如果已经是最终状态就不再进行更新
*
* @param status 状态
* @return 是否
*/
public static boolean isEndStatus(Integer status) {
return ObjectUtils.equalsAny(status,
APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus(),
RETURN.getStatus(), APPROVING.getStatus());
}
}

View File

@ -1,86 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Comparator;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - BPM 流程分类")
@RestController
@RequestMapping("/bpm/category")
@Validated
public class BpmCategoryController {
@Resource
private BpmCategoryService categoryService;
@PostMapping("/create")
@Operation(summary = "创建流程分类")
@PreAuthorize("@ss.hasPermission('bpm:category:create')")
public CommonResult<Long> createCategory(@Valid @RequestBody BpmCategorySaveReqVO createReqVO) {
return success(categoryService.createCategory(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新流程分类")
@PreAuthorize("@ss.hasPermission('bpm:category:update')")
public CommonResult<Boolean> updateCategory(@Valid @RequestBody BpmCategorySaveReqVO updateReqVO) {
categoryService.updateCategory(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除流程分类")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:category:delete')")
public CommonResult<Boolean> deleteCategory(@RequestParam("id") Long id) {
categoryService.deleteCategory(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得流程分类")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:category:query')")
public CommonResult<BpmCategoryRespVO> getCategory(@RequestParam("id") Long id) {
BpmCategoryDO category = categoryService.getCategory(id);
return success(BeanUtils.toBean(category, BpmCategoryRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得流程分类分页")
@PreAuthorize("@ss.hasPermission('bpm:category:query')")
public CommonResult<PageResult<BpmCategoryRespVO>> getCategoryPage(@Valid BpmCategoryPageReqVO pageReqVO) {
PageResult<BpmCategoryDO> pageResult = categoryService.getCategoryPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, BpmCategoryRespVO.class));
}
@GetMapping("/simple-list")
@Operation(summary = "获取流程分类的精简信息列表", description = "只包含被开启的分类,主要用于前端的下拉选项")
public CommonResult<List<BpmCategoryRespVO>> getCategorySimpleList() {
List<BpmCategoryDO> list = categoryService.getCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus());
list.sort(Comparator.comparingInt(BpmCategoryDO::getSort));
return success(convertList(list, category -> new BpmCategoryRespVO().setId(category.getId())
.setName(category.getName()).setCode(category.getCode())));
}
}

View File

@ -1,73 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessExpressionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - BPM 流程表达式")
@RestController
@RequestMapping("/bpm/process-expression")
@Validated
public class BpmProcessExpressionController {
@Resource
private BpmProcessExpressionService processExpressionService;
@PostMapping("/create")
@Operation(summary = "创建流程表达式")
@PreAuthorize("@ss.hasPermission('bpm:process-expression:create')")
public CommonResult<Long> createProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO createReqVO) {
return success(processExpressionService.createProcessExpression(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新流程表达式")
@PreAuthorize("@ss.hasPermission('bpm:process-expression:update')")
public CommonResult<Boolean> updateProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO updateReqVO) {
processExpressionService.updateProcessExpression(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除流程表达式")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:process-expression:delete')")
public CommonResult<Boolean> deleteProcessExpression(@RequestParam("id") Long id) {
processExpressionService.deleteProcessExpression(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得流程表达式")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:process-expression:query')")
public CommonResult<BpmProcessExpressionRespVO> getProcessExpression(@RequestParam("id") Long id) {
BpmProcessExpressionDO processExpression = processExpressionService.getProcessExpression(id);
return success(BeanUtils.toBean(processExpression, BpmProcessExpressionRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得流程表达式分页")
@PreAuthorize("@ss.hasPermission('bpm:process-expression:query')")
public CommonResult<PageResult<BpmProcessExpressionRespVO>> getProcessExpressionPage(
@Valid BpmProcessExpressionPageReqVO pageReqVO) {
PageResult<BpmProcessExpressionDO> pageResult = processExpressionService.getProcessExpressionPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, BpmProcessExpressionRespVO.class));
}
}

View File

@ -1,73 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessListenerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - BPM 流程监听器")
@RestController
@RequestMapping("/bpm/process-listener")
@Validated
public class BpmProcessListenerController {
@Resource
private BpmProcessListenerService processListenerService;
@PostMapping("/create")
@Operation(summary = "创建流程监听器")
@PreAuthorize("@ss.hasPermission('bpm:process-listener:create')")
public CommonResult<Long> createProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO createReqVO) {
return success(processListenerService.createProcessListener(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新流程监听器")
@PreAuthorize("@ss.hasPermission('bpm:process-listener:update')")
public CommonResult<Boolean> updateProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO updateReqVO) {
processListenerService.updateProcessListener(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除流程监听器")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:process-listener:delete')")
public CommonResult<Boolean> deleteProcessListener(@RequestParam("id") Long id) {
processListenerService.deleteProcessListener(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得流程监听器")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:process-listener:query')")
public CommonResult<BpmProcessListenerRespVO> getProcessListener(@RequestParam("id") Long id) {
BpmProcessListenerDO processListener = processListenerService.getProcessListener(id);
return success(BeanUtils.toBean(processListener, BpmProcessListenerRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得流程监听器分页")
@PreAuthorize("@ss.hasPermission('bpm:process-listener:query')")
public CommonResult<PageResult<BpmProcessListenerRespVO>> getProcessListenerPage(
@Valid BpmProcessListenerPageReqVO pageReqVO) {
PageResult<BpmProcessListenerDO> pageResult = processListenerService.getProcessListenerPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, BpmProcessListenerRespVO.class));
}
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - BPM 流程分类分页 Request VO")
@Data
public class BpmCategoryPageReqVO extends PageParam {
@Schema(description = "分类名", example = "王五")
private String name;
@Schema(description = "分类标志", example = "OA")
private String code;
@Schema(description = "分类状态", example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - BPM 流程分类 Response VO")
@Data
public class BpmCategoryRespVO {
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167")
private Long id;
@Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
private String name;
@Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA")
private String code;
@Schema(description = "分类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜")
private String description;
@Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer sort;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@ -1,37 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - BPM 流程分类新增/修改 Request VO")
@Data
public class BpmCategorySaveReqVO {
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167")
private Long id;
@Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
@NotEmpty(message = "分类名不能为空")
private String name;
@Schema(description = "分类描述", example = "你猜")
private String description;
@Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA")
@NotEmpty(message = "分类标志不能为空")
private String code;
@Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "分类状态不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "分类排序不能为空")
private Integer sort;
}

View File

@ -1,33 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - BPM 流程表达式分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmProcessExpressionPageReqVO extends PageParam {
@Schema(description = "表达式名字", example = "李四")
private String name;
@Schema(description = "表达式状态", example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - BPM 流程表达式 Response VO")
@Data
public class BpmProcessExpressionRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870")
@ExcelProperty("编号")
private Long id;
@Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@ExcelProperty("表达式名字")
private String name;
@Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED)
private String expression;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@ -1,27 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - BPM 流程表达式新增/修改 Request VO")
@Data
public class BpmProcessExpressionSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870")
private Long id;
@Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@NotEmpty(message = "表达式名字不能为空")
private String name;
@Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "表达式状态不能为空")
private Integer status;
@Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "表达式不能为空")
private String expression;
}

View File

@ -1,34 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 动态表单创建/更新 Request VO")
@Data
public class BpmFormSaveReqVO {
@Schema(description = "表单编号", example = "1024")
private Long id;
@Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
@NotNull(message = "表单名称不能为空")
private String name;
@Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单的配置不能为空")
private String conf;
@Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "表单项的数组不能为空")
private List<String> fields;
@Schema(description = "表单状态-参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "表单状态不能为空")
private Integer status;
@Schema(description = "备注", example = "我是备注")
private String remark;
}

View File

@ -1,30 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - BPM 流程监听器分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmProcessListenerPageReqVO extends PageParam {
@Schema(description = "监听器名字", example = "赵六")
private String name;
@Schema(description = "监听器类型", example = "execution")
private String type;
@Schema(description = "监听事件", example = "start")
private String event;
@Schema(description = "状态", example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
}

View File

@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - BPM 流程监听器 Response VO")
@Data
public class BpmProcessListenerRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089")
private Long id;
@Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
private String name;
@Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution")
private String type;
@Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start")
private String event;
@Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class")
private String valueType;
@Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED)
private String value;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - BPM 流程监听器新增/修改 Request VO")
@Data
public class BpmProcessListenerSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089")
private Long id;
@Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
@NotEmpty(message = "监听器名字不能为空")
private String name;
@Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution")
@NotEmpty(message = "监听器类型不能为空")
private String type;
@Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "监听器状态不能为空")
private Integer status;
@Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start")
@NotEmpty(message = "监听事件不能为空")
private String event;
@Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class")
@NotEmpty(message = "监听器值类型不能为空")
private String valueType;
@Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "监听器值不能为空")
private String value;
}

View File

@ -1,40 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO")
@Data
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, example = "A233")
private String processInstanceId;
@Schema(description = "流程实例的名称")
private String processInstanceName;
@Schema(description = "流程实例的发起时间")
private LocalDateTime processInstanceStartTime;
@Schema(description = "发起抄送的任务编号")
private String taskId;
@Schema(description = "发起抄送的任务名称")
private String taskName;
@Schema(description = "抄送人")
private String creator;
@Schema(description = "抄送人昵称")
private String creatorName;
@Schema(description = "抄送时间")
private LocalDateTime createTime;
}

View File

@ -1,29 +0,0 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import java.util.Set;
@Schema(description = "管理后台 - 加签任务的创建(加签) Request VO")
@Data
public class BpmTaskSignCreateReqVO {
@Schema(description = "需要加签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "加签的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888")
@NotEmpty(message = "加签用户不能为空")
private Set<Long> userIds;
@Schema(description = "加签类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "before")
@NotEmpty(message = "加签类型不能为空")
private String type; // 参见 BpmTaskSignTypeEnum 枚举
@Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要加签")
@NotEmpty(message = "加签原因不能为空")
private String reason;
}

View File

@ -1,50 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* BPM 流程分类 DO
*
* @author 芋道源码
*/
@TableName("bpm_category")
@KeySequence("bpm_category_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmCategoryDO extends BaseDO {
/**
* 分类编号
*/
@TableId
private Long id;
/**
* 分类名
*/
private String name;
/**
* 分类标志
*/
private String code;
/**
* 分类描述
*/
private String description;
/**
* 分类状态
*
* 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
*/
private Integer status;
/**
* 分类排序
*/
private Integer sort;
}

View File

@ -1,45 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* BPM 流程表达式 DO
*
* @author 芋道源码
*/
@TableName("bpm_process_expression")
@KeySequence("bpm_process_expression_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmProcessExpressionDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 表达式名字
*/
private String name;
/**
* 表达式状态
*
* 枚举 {@link TODO common_status 对应的类}
*/
private Integer status;
/**
* 表达式
*/
private String expression;
}

View File

@ -1,70 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* BPM 流程监听器 DO
*
* 目的本质上它是流程监听器的模版用于 BPMN 在设计时直接选择这些模版
*
* @author 芋道源码
*/
@TableName(value = "bpm_process_listener")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmProcessListenerDO extends BaseDO {
/**
* 主键 ID自增
*/
@TableId
private Long id;
/**
* 监听器名字
*/
private String name;
/**
* 状态
*
* 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
*/
private Integer status;
/**
* 监听类型
*
* 枚举 {@link cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerType}
*
* 1. executionExecutionListener <a href="https://tkjohn.github.io/flowable-userguide/#executionListeners">执行监听器</a>
* 2. taskTaskListener <a href="https://tkjohn.github.io/flowable-userguide/#taskListeners">任务监听器</a>
*/
private String type;
/**
* 监听事件
*
* execution startend
* task create 创建assignment 指派complete 完成delete 删除update 更新timeout 超时
*/
private String event;
/**
* 值类型
*
* 1. classJava ExecutionListener 需要 {@link org.flowable.engine.delegate.JavaDelegate}TaskListener 需要 {@link org.flowable.engine.delegate.TaskListener}
* 2. delegateExpression委托表达式 class 的基础上需要注册到 Spring 容器里后续表达式通过 Spring Bean 名称即可
* 3. expression表达式一个普通类的普通方法将这个普通类注册到 Spring 容器中然后表达式中还可以执行这个类中的方法
*/
private String valueType;
/**
*
*/
private String value;
}

View File

@ -1,46 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.category;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
* BPM 流程分类 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface BpmCategoryMapper extends BaseMapperX<BpmCategoryDO> {
default PageResult<BpmCategoryDO> selectPage(BpmCategoryPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmCategoryDO>()
.likeIfPresent(BpmCategoryDO::getName, reqVO.getName())
.likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode())
.eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus())
.betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime())
.orderByAsc(BpmCategoryDO::getSort));
}
default BpmCategoryDO selectByName(String name) {
return selectOne(BpmCategoryDO::getName, name);
}
default BpmCategoryDO selectByCode(String code) {
return selectOne(BpmCategoryDO::getCode, code);
}
default List<BpmCategoryDO> selectListByCode(Collection<String> codes) {
return selectList(BpmCategoryDO::getCode, codes);
}
default List<BpmCategoryDO> selectListByStatus(Integer status) {
return selectList(BpmCategoryDO::getStatus, status);
}
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface BpmProcessDefinitionInfoMapper extends BaseMapperX<BpmProcessDefinitionInfoDO> {
default List<BpmProcessDefinitionInfoDO> selectListByProcessDefinitionIds(Collection<String> processDefinitionIds) {
return selectList(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionIds);
}
default BpmProcessDefinitionInfoDO selectByProcessDefinitionId(String processDefinitionId) {
return selectOne(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionId);
}
}

View File

@ -1,26 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;
import org.apache.ibatis.annotations.Mapper;
/**
* BPM 流程表达式 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface BpmProcessExpressionMapper extends BaseMapperX<BpmProcessExpressionDO> {
default PageResult<BpmProcessExpressionDO> selectPage(BpmProcessExpressionPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessExpressionDO>()
.likeIfPresent(BpmProcessExpressionDO::getName, reqVO.getName())
.eqIfPresent(BpmProcessExpressionDO::getStatus, reqVO.getStatus())
.betweenIfPresent(BpmProcessExpressionDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(BpmProcessExpressionDO::getId));
}
}

View File

@ -1,27 +0,0 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;
import org.apache.ibatis.annotations.Mapper;
/**
* BPM 流程监听器 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface BpmProcessListenerMapper extends BaseMapperX<BpmProcessListenerDO> {
default PageResult<BpmProcessListenerDO> selectPage(BpmProcessListenerPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessListenerDO>()
.likeIfPresent(BpmProcessListenerDO::getName, reqVO.getName())
.eqIfPresent(BpmProcessListenerDO::getType, reqVO.getType())
.eqIfPresent(BpmProcessListenerDO::getEvent, reqVO.getEvent())
.eqIfPresent(BpmProcessListenerDO::getStatus, reqVO.getStatus())
.orderByDesc(BpmProcessListenerDO::getId));
}
}

View File

@ -1,50 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
import lombok.Setter;
import org.flowable.bpmn.model.Activity;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 自定义的串行多个流程任务的 assignee 负责人的分配
*
* 本质上实现和 {@link BpmParallelMultiInstanceBehavior} 一样只是继承的类不一样
*
* @author 芋道源码
*/
@Setter
public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior {
private BpmTaskCandidateInvoker taskCandidateInvoker;
public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
super(activity, innerActivityBehavior);
}
/**
* 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似
*
* 差异的点是在第二步的时候需要返回 LinkedHashSet 集合因为它需要有序
*/
@Override
protected int resolveNrOfInstances(DelegateExecution execution) {
// 第一步设置 collectionVariable CollectionVariable
// execution.getVariable() 读取所有任务处理人的 key
super.collectionExpression = null; // collectionExpression collectionVariable 是互斥的
super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
// execution.getVariable() 读取当前所有任务处理的人的 key
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
// 第二步获取任务的所有处理人
Set<Long> assigneeUserIds = new LinkedHashSet<>(taskCandidateInvoker.calculateUsers(execution)); // 保证有序
execution.setVariable(super.collectionVariable, assigneeUserIds);
return assigneeUserIds.size();
}
}

View File

@ -1,119 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import java.util.HashMap;
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.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.TASK_CREATE_FAIL_NO_CANDIDATE_USER;
/**
* {@link BpmTaskCandidateStrategy} 的调用者用于调用对应的策略实现任务的候选人的计算
*
* @author 芋道源码
*/
@Slf4j
public class BpmTaskCandidateInvoker {
private final Map<BpmTaskCandidateStrategyEnum, BpmTaskCandidateStrategy> strategyMap = new HashMap<>();
private final AdminUserApi adminUserApi;
public BpmTaskCandidateInvoker(List<BpmTaskCandidateStrategy> strategyList,
AdminUserApi adminUserApi) {
strategyList.forEach(strategy -> {
BpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy);
Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy());
});
this.adminUserApi = adminUserApi;
}
/**
* 校验流程模型的任务分配规则全部都配置了
* 目的如果有规则未配置会导致流程任务找不到负责人进而流程无法进行下去
*
* @param bpmnBytes BPMN XML
*/
public void validateBpmnConfig(byte[] bpmnBytes) {
BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes);
assert bpmnModel != null;
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
// 遍历所有的 UserTask校验审批人配置
userTaskList.forEach(userTask -> {
// 1. 非空校验
Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask);
String param = BpmnModelUtils.parseCandidateParam(userTask);
if (strategy == null) {
throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
}
BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);
if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
}
// 2. 具体策略校验
getCandidateStrategy(strategy).validateParam(param);
});
}
/**
* 计算任务的候选人
*
* @param execution 执行任务
* @return 用户编号集合
*/
@DataPermission(enable = false) // 忽略数据权限避免因为过滤导致找不到候选人
public Set<Long> calculateUsers(DelegateExecution execution) {
Integer strategy = BpmnModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
String param = BpmnModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
// 1.1 计算任务的候选人
Set<Long> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
// 1.2 移除被禁用的用户
removeDisableUsers(userIds);
// 2. 校验是否有候选人
if (CollUtil.isEmpty(userIds)) {
log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param);
throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
}
return userIds;
}
@VisibleForTesting
void removeDisableUsers(Set<Long> assigneeUserIds) {
if (CollUtil.isEmpty(assigneeUserIds)) {
return;
}
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIds);
assigneeUserIds.removeIf(id -> {
AdminUserRespDTO user = userMap.get(id);
return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
});
}
private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) {
BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy);
Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy);
BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
Assert.notNull(strategyObj, "策略(%s) 不存在", strategy);
return strategyObj;
}
}

View File

@ -1,48 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import org.flowable.engine.delegate.DelegateExecution;
import java.util.Set;
/**
* BPM 任务的候选人的策略接口
*
* 例如说分配审批人
*
* @author 芋道源码
*/
public interface BpmTaskCandidateStrategy {
/**
* 对应策略
*
* @return 策略
*/
BpmTaskCandidateStrategyEnum getStrategy();
/**
* 校验参数
*
* @param param 参数
*/
void validateParam(String param);
/**
* 基于执行任务获得任务的候选用户们
*
* @param execution 执行任务
* @return 用户编号集合
*/
Set<Long> calculateUsers(DelegateExecution execution, String param);
/**
* 是否一定要输入参数
*
* @return 是否
*/
default boolean isParamRequired() {
return true;
}
}

View File

@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.expression;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import jakarta.annotation.Resource;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* 分配给发起人审批的 Expression 流程表达式
*
* @author 芋道源码
*/
@Component
public class BpmTaskAssignStartUserExpression {
@Resource
private BpmProcessInstanceService processInstanceService;
/**
* 计算审批的候选人
*
* @param execution 流程执行实体
* @return 发起人
*/
public Set<Long> calculateUsers(ExecutionEntityImpl execution) {
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId());
return SetUtils.asSet(startUserId);
}
}

View File

@ -1,46 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* 部门的负责人 {@link BpmTaskCandidateStrategy} 实现类
*
* @author kyle
*/
@Component
public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy {
@Resource
private DeptApi deptApi;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.DEPT_LEADER;
}
@Override
public void validateParam(String param) {
Set<Long> deptIds = StrUtils.splitToLongSet(param);
deptApi.validateDeptList(deptIds);
}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
Set<Long> deptIds = StrUtils.splitToLongSet(param);
List<DeptRespDTO> depts = deptApi.getDeptList(deptIds);
return convertSet(depts, DeptRespDTO::getLeaderUserId);
}
}

View File

@ -1,49 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* 部门的成员 {@link BpmTaskCandidateStrategy} 实现类
*
* @author kyle
*/
@Component
public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy {
@Resource
private DeptApi deptApi;
@Resource
private AdminUserApi adminUserApi;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.DEPT_MEMBER;
}
@Override
public void validateParam(String param) {
Set<Long> deptIds = StrUtils.splitToLongSet(param);
deptApi.validateDeptList(deptIds);
}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
Set<Long> deptIds = StrUtils.splitToLongSet(param);
List<AdminUserRespDTO> users = adminUserApi.getUserListByDeptIds(deptIds);
return convertSet(users, AdminUserRespDTO::getId);
}
}

View File

@ -1,36 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import org.dromara.hutool.core.convert.Convert;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* 流程表达式 {@link BpmTaskCandidateStrategy} 实现类
*
* @author 芋道源码
*/
@Component
public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy {
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.EXPRESSION;
}
@Override
public void validateParam(String param) {
// do nothing 因为它基本做不了校验
}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
Object result = FlowableUtils.getExpressionValue(execution, param);
return Convert.toSet(Long.class, result);
}
}

View File

@ -1,47 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
/**
* 用户组 {@link BpmTaskCandidateStrategy} 实现类
*
* @author kyle
*/
@Component
public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy {
@Resource
private BpmUserGroupService userGroupService;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.USER_GROUP;
}
@Override
public void validateParam(String param) {
Set<Long> groupIds = StrUtils.splitToLongSet(param);
userGroupService.getUserGroupList(groupIds);
}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
Set<Long> groupIds = StrUtils.splitToLongSet(param);
List<BpmUserGroupDO> groups = userGroupService.getUserGroupList(groupIds);
return convertSetByFlatMap(groups, BpmUserGroupDO::getUserIds, Collection::stream);
}
}

View File

@ -1,49 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* 岗位 {@link BpmTaskCandidateStrategy} 实现类
*
* @author kyle
*/
@Component
public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy {
@Resource
private PostApi postApi;
@Resource
private AdminUserApi adminUserApi;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.POST;
}
@Override
public void validateParam(String param) {
Set<Long> postIds = StrUtils.splitToLongSet(param);
postApi.validPostList(postIds);
}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
Set<Long> postIds = StrUtils.splitToLongSet(param);
List<AdminUserRespDTO> users = adminUserApi.getUserListByPostIds(postIds);
return convertSet(users, AdminUserRespDTO::getId);
}
}

View File

@ -1,44 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* 角色 {@link BpmTaskCandidateStrategy} 实现类
*
* @author kyle
*/
@Component
public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy {
@Resource
private RoleApi roleApi;
@Resource
private PermissionApi permissionApi;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.ROLE;
}
@Override
public void validateParam(String param) {
Set<Long> roleIds = StrUtils.splitToLongSet(param);
roleApi.validRoleList(roleIds);
}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
Set<Long> roleIds = StrUtils.splitToLongSet(param);
return permissionApi.getUserRoleIdListByRoleIds(roleIds);
}
}

View File

@ -1,76 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
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.task.BpmProcessInstanceService;
import jakarta.annotation.Resource;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类
*
* @author 芋道源码
*/
@Component
public class BpmTaskCandidateStartUserSelectStrategy implements BpmTaskCandidateStrategy {
@Resource
@Lazy // 延迟加载避免循环依赖
private BpmProcessInstanceService processInstanceService;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.START_USER_SELECT;
}
@Override
public void validateParam(String param) {}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId());
Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId());
Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance);
Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空",
execution.getProcessInstanceId());
// 获得审批人
List<Long> assignees = startUserSelectAssignees.get(execution.getCurrentActivityId());
return new LinkedHashSet<>(assignees);
}
@Override
public boolean isParamRequired() {
return false;
}
/**
* 获得发起人自选审批人的 UserTask 列表
*
* @param bpmnModel BPMN 模型
* @return UserTask 列表
*/
public static List<UserTask> getStartUserSelectUserTaskList(BpmnModel bpmnModel) {
if (bpmnModel == null) {
return null;
}
List<UserTask> userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
if (CollUtil.isEmpty(userTaskList)) {
return null;
}
userTaskList.removeIf(userTask -> !Objects.equals(BpmnModelUtils.parseCandidateStrategy(userTask),
BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy()));
return userTaskList;
}
}

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
import java.util.Set;
/**
* 用户 {@link BpmTaskCandidateStrategy} 实现类
*
* @author kyle
*/
@Component
public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy {
@Resource
private AdminUserApi adminUserApi;
@Override
public BpmTaskCandidateStrategyEnum getStrategy() {
return BpmTaskCandidateStrategyEnum.USER;
}
@Override
public void validateParam(String param) {
adminUserApi.validateUserList(StrUtils.splitToLongSet(param));
}
@Override
public Set<Long> calculateUsers(DelegateExecution execution, String param) {
return StrUtils.splitToLongSet(param);
}
}

View File

@ -1,40 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
import org.flowable.engine.runtime.ProcessInstance;
/**
* BPM 通用常量
*
* @author 芋道源码
*/
public class BpmConstants {
/**
* 流程实例的变量 - 状态
*
* @see ProcessInstance#getProcessVariables()
*/
public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS";
/**
* 流程实例的变量 - 发起用户选择的审批人 Map
*
* @see ProcessInstance#getProcessVariables()
*/
public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES";
/**
* 任务的变量 - 状态
*
* @see org.flowable.task.api.Task#getTaskLocalVariables()
*/
public static final String TASK_VARIABLE_STATUS = "TASK_STATUS";
/**
* 任务的变量 - 理由
*
* 例如说审批通过不通过的理由
*
* @see org.flowable.task.api.Task#getTaskLocalVariables()
*/
public static final String TASK_VARIABLE_REASON = "TASK_REASON";
}

View File

@ -1,41 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* BPM 任务的候选人策略枚举
*
* 例如说分配给指定人审批
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmTaskCandidateStrategyEnum {
ROLE(10, "角色"),
DEPT_MEMBER(20, "部门的成员"), // 包括负责人
DEPT_LEADER(21, "部门的负责人"),
POST(22, "岗位"),
USER(30, "用户"),
START_USER_SELECT(35, "发起人自选"), // 申请人自己可在提交申请时选择此节点的审批人
USER_GROUP(40, "用户组"),
EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager
;
/**
* 类型
*/
private final Integer strategy;
/**
* 描述
*/
private final String description;
public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) {
return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values());
}
}

View File

@ -1,26 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
/**
* BPMN XML 常量信息
*
* @author 芋道源码
*/
public interface BpmnModelConstants {
String BPMN_FILE_SUFFIX = ".bpmn";
/**
* BPMN 中的命名空间
*/
String NAMESPACE = "http://flowable.org/bpmn";
/**
* BPMN UserTask 的扩展属性用于标记候选人策略
*/
String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy";
/**
* BPMN UserTask 的扩展属性用于标记候选人参数
*/
String USER_TASK_CANDIDATE_PARAM = "candidateParam";
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.exection;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
/**
* 类型为 class ExecutionListener 监听器示例
*
* @author 芋道源码
*/
@Slf4j
public class DemoDelegateClassExecutionListener implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(),
execution.getCurrentFlowableListener().getFieldExtensions());
}
}

View File

@ -1,23 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.exection;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
/**
* 类型为 delegateExpression ExecutionListener 监听器示例
*
* {@link DemoDelegateClassExecutionListener} 的差异是需要注册到 Spring
*/
@Component
@Slf4j
public class DemoDelegateExpressionExecutionListener implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(),
execution.getCurrentFlowableListener().getFieldExtensions());
}
}

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.exection;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.springframework.stereotype.Component;
/**
* 类型为 expression ExecutionListener 监听器示例
*
* {@link DemoDelegateClassExecutionListener} 的差异是需要注册到 Spring 但不用实现 {@link org.flowable.engine.delegate.JavaDelegate} 接口
*/
@Component
@Slf4j
public class DemoSpringExpressionExecutionListener {
public void execute(DelegateExecution execution) {
log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(),
execution.getCurrentFlowableListener().getFieldExtensions());
}
}

View File

@ -1,20 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
/**
* 类型为 class TaskListener 监听器示例
*
* @author 芋道源码
*/
@Slf4j
public class DemoDelegateClassTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
log.info("[execute][task({}) 被调用]", delegateTask.getId());
}
}

View File

@ -1,22 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.stereotype.Component;
/**
* 类型为 delegateExpression TaskListener 监听器示例
*
* @author 芋道源码
*/
@Component
@Slf4j
public class DemoDelegateExpressionTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
log.info("[execute][task({}) 被调用]", delegateTask.getId());
}
}

View File

@ -1,20 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.demo.task;
import lombok.extern.slf4j.Slf4j;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.stereotype.Component;
/**
* 类型为 expression TaskListener 监听器示例
*
* @author 芋道源码
*/
@Slf4j
@Component
public class DemoSpringExpressionTaskListener {
public void notify(DelegateTask delegateTask) {
log.info("[execute][task({}) 被调用]", delegateTask.getId());
}
}

View File

@ -1,173 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.api.variable.VariableContainer;
import org.flowable.common.engine.impl.el.ExpressionManager;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.TaskInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Flowable 相关的工具方法
*
* @author 芋道源码
*/
public class FlowableUtils {
// ========== User 相关的工具方法 ==========
public static void setAuthenticatedUserId(Long userId) {
Authentication.setAuthenticatedUserId(String.valueOf(userId));
}
public static void clearAuthenticatedUserId() {
Authentication.setAuthenticatedUserId(null);
}
// ========== Execution 相关的工具方法 ==========
/**
* 格式化多实例并签或签 collectionVariable 变量多实例对应的多审批人列表
*
* @param activityId 活动编号
* @return collectionVariable 变量
*/
public static String formatExecutionCollectionVariable(String activityId) {
return activityId + "_assignees";
}
/**
* 格式化多实例并签或签 collectionElementVariable 变量当前实例对应的一个审批人
*
* @param activityId 活动编号
* @return collectionElementVariable 变量
*/
public static String formatExecutionCollectionElementVariable(String activityId) {
return activityId + "_assignee";
}
// ========== ProcessInstance 相关的工具方法 ==========
public static Integer getProcessInstanceStatus(ProcessInstance processInstance) {
return getProcessInstanceStatus(processInstance.getProcessVariables());
}
public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) {
return getProcessInstanceStatus(processInstance.getProcessVariables());
}
/**
* 获得流程实例的状态
*
* @param processVariables 流程实例的 variables
* @return 状态
*/
private static Integer getProcessInstanceStatus(Map<String, Object> processVariables) {
return (Integer) processVariables.get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
}
/**
* 获得流程实例的表单
*
* @param processInstance 流程实例
* @return 表单
*/
public static Map<String, Object> getProcessInstanceFormVariable(HistoricProcessInstance processInstance) {
Map<String, Object> formVariables = new HashMap<>(processInstance.getProcessVariables());
filterProcessInstanceFormVariable(formVariables);
return formVariables;
}
/**
* 过滤流程实例的表单
*
* 为什么要过滤目前使用 processVariables 存储所有流程实例的拓展字段需要过滤掉一部分的系统字段从而实现表单的展示
*
* @param processVariables 流程实例的 variables
* @return 过滤后的表单
*/
public static Map<String, Object> filterProcessInstanceFormVariable(Map<String, Object> processVariables) {
processVariables.remove(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
return processVariables;
}
/**
* 获得流程实例的发起用户选择的审批人 Map
*
* @param processInstance 流程实例
* @return 发起用户选择的审批人 Map
*/
@SuppressWarnings("unchecked")
public static Map<String, List<Long>> getStartUserSelectAssignees(ProcessInstance processInstance) {
return (Map<String, List<Long>>) processInstance.getProcessVariables().get(
BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
}
// ========== Task 相关的工具方法 ==========
/**
* 获得任务的状态
*
* @param task 任务
* @return 状态
*/
public static Integer getTaskStatus(TaskInfo task) {
return (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS);
}
/**
* 获得任务的审批原因
*
* @param task 任务
* @return 审批原因
*/
public static String getTaskReason(TaskInfo task) {
return (String) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_REASON);
}
/**
* 获得任务的表单
*
* @param task 任务
* @return 表单
*/
public static Map<String, Object> getTaskFormVariable(TaskInfo task) {
Map<String, Object> formVariables = new HashMap<>(task.getTaskLocalVariables());
filterTaskFormVariable(formVariables);
return formVariables;
}
/**
* 过滤任务的表单
*
* 为什么要过滤目前使用 taskLocalVariables 存储所有任务的拓展字段需要过滤掉一部分的系统字段从而实现表单的展示
*
* @param taskLocalVariables 任务的 taskLocalVariables
* @return 过滤后的表单
*/
public static Map<String, Object> filterTaskFormVariable(Map<String, Object> taskLocalVariables) {
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_STATUS);
taskLocalVariables.remove(BpmConstants.TASK_VARIABLE_REASON);
return taskLocalVariables;
}
// ========== Expression 相关的工具方法 ==========
public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
assert processEngineConfiguration != null;
ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
assert expressionManager != null;
Expression expression = expressionManager.createExpression(expressionString);
return expression.getValue(variableContainer);
}
}

View File

@ -1,85 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.definition;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import jakarta.validation.Valid;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/**
* BPM 流程分类 Service 接口
*
* @author 芋道源码
*/
public interface BpmCategoryService {
/**
* 创建流程分类
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createCategory(@Valid BpmCategorySaveReqVO createReqVO);
/**
* 更新流程分类
*
* @param updateReqVO 更新信息
*/
void updateCategory(@Valid BpmCategorySaveReqVO updateReqVO);
/**
* 删除流程分类
*
* @param id 编号
*/
void deleteCategory(Long id);
/**
* 获得流程分类
*
* @param id 编号
* @return BPM 流程分类
*/
BpmCategoryDO getCategory(Long id);
/**
* 获得流程分类分页
*
* @param pageReqVO 分页查询
* @return 流程分类分页
*/
PageResult<BpmCategoryDO> getCategoryPage(BpmCategoryPageReqVO pageReqVO);
/**
* 获得流程分类 Map基于指定编码
*
* @param codes 编号数组
* @return 流程分类 Map
*/
default Map<String, BpmCategoryDO> getCategoryMap(Collection<String> codes) {
return convertMap(getCategoryListByCode(codes), BpmCategoryDO::getCode);
}
/**
* 获得流程分类列表基于指定编码
*
* @return 流程分类列表
*/
List<BpmCategoryDO> getCategoryListByCode(Collection<String> codes);
/**
* 获得流程分类列表基于指定状态
*
* @param status 状态
* @return 流程分类列表
*/
List<BpmCategoryDO> getCategoryListByStatus(Integer status);
}

View File

@ -1,113 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.definition;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* BPM 流程分类 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class BpmCategoryServiceImpl implements BpmCategoryService {
@Resource
private BpmCategoryMapper bpmCategoryMapper;
@Override
public Long createCategory(BpmCategorySaveReqVO createReqVO) {
// 校验唯一
validateCategoryNameUnique(createReqVO);
validateCategoryCodeUnique(createReqVO);
// 插入
BpmCategoryDO category = BeanUtils.toBean(createReqVO, BpmCategoryDO.class);
bpmCategoryMapper.insert(category);
return category.getId();
}
@Override
public void updateCategory(BpmCategorySaveReqVO updateReqVO) {
// 校验存在
validateCategoryExists(updateReqVO.getId());
validateCategoryNameUnique(updateReqVO);
validateCategoryCodeUnique(updateReqVO);
// 更新
BpmCategoryDO updateObj = BeanUtils.toBean(updateReqVO, BpmCategoryDO.class);
bpmCategoryMapper.updateById(updateObj);
}
private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) {
BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName());
if (category == null
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
return;
}
throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName());
}
private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) {
BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode());
if (category == null
|| ObjUtil.equal(category.getId(), updateReqVO.getId())) {
return;
}
throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode());
}
@Override
public void deleteCategory(Long id) {
// 校验存在
validateCategoryExists(id);
// 删除
bpmCategoryMapper.deleteById(id);
}
private void validateCategoryExists(Long id) {
if (bpmCategoryMapper.selectById(id) == null) {
throw exception(CATEGORY_NOT_EXISTS);
}
}
@Override
public BpmCategoryDO getCategory(Long id) {
return bpmCategoryMapper.selectById(id);
}
@Override
public PageResult<BpmCategoryDO> getCategoryPage(BpmCategoryPageReqVO pageReqVO) {
return bpmCategoryMapper.selectPage(pageReqVO);
}
@Override
public List<BpmCategoryDO> getCategoryListByCode(Collection<String> codes) {
if (CollUtil.isEmpty(codes)) {
return Collections.emptyList();
}
return bpmCategoryMapper.selectListByCode(codes);
}
@Override
public List<BpmCategoryDO> getCategoryListByStatus(Integer status) {
return bpmCategoryMapper.selectListByStatus(status);
}
}

View File

@ -1,54 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.definition;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO;
import jakarta.validation.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
/**
* BPM 流程表达式 Service 接口
*
* @author 芋道源码
*/
public interface BpmProcessExpressionService {
/**
* 创建流程表达式
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createProcessExpression(@Valid BpmProcessExpressionSaveReqVO createReqVO);
/**
* 更新流程表达式
*
* @param updateReqVO 更新信息
*/
void updateProcessExpression(@Valid BpmProcessExpressionSaveReqVO updateReqVO);
/**
* 删除流程表达式
*
* @param id 编号
*/
void deleteProcessExpression(Long id);
/**
* 获得流程表达式
*
* @param id 编号
* @return 流程表达式
*/
BpmProcessExpressionDO getProcessExpression(Long id);
/**
* 获得流程表达式分页
*
* @param pageReqVO 分页查询
* @return 流程表达式分页
*/
PageResult<BpmProcessExpressionDO> getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO);
}

View File

@ -1,72 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.definition;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessExpressionMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* BPM 流程表达式 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class BpmProcessExpressionServiceImpl implements BpmProcessExpressionService {
@Resource
private BpmProcessExpressionMapper processExpressionMapper;
@Override
public Long createProcessExpression(BpmProcessExpressionSaveReqVO createReqVO) {
// 插入
BpmProcessExpressionDO processExpression = BeanUtils.toBean(createReqVO, BpmProcessExpressionDO.class);
processExpressionMapper.insert(processExpression);
// 返回
return processExpression.getId();
}
@Override
public void updateProcessExpression(BpmProcessExpressionSaveReqVO updateReqVO) {
// 校验存在
validateProcessExpressionExists(updateReqVO.getId());
// 更新
BpmProcessExpressionDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessExpressionDO.class);
processExpressionMapper.updateById(updateObj);
}
@Override
public void deleteProcessExpression(Long id) {
// 校验存在
validateProcessExpressionExists(id);
// 删除
processExpressionMapper.deleteById(id);
}
private void validateProcessExpressionExists(Long id) {
if (processExpressionMapper.selectById(id) == null) {
throw exception(PROCESS_EXPRESSION_NOT_EXISTS);
}
}
@Override
public BpmProcessExpressionDO getProcessExpression(Long id) {
return processExpressionMapper.selectById(id);
}
@Override
public PageResult<BpmProcessExpressionDO> getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO) {
return processExpressionMapper.selectPage(pageReqVO);
}
}

View File

@ -1,54 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.definition;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO;
import jakarta.validation.*;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
/**
* BPM 流程监听器 Service 接口
*
* @author 芋道源码
*/
public interface BpmProcessListenerService {
/**
* 创建流程监听器
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createProcessListener(@Valid BpmProcessListenerSaveReqVO createReqVO);
/**
* 更新流程监听器
*
* @param updateReqVO 更新信息
*/
void updateProcessListener(@Valid BpmProcessListenerSaveReqVO updateReqVO);
/**
* 删除流程监听器
*
* @param id 编号
*/
void deleteProcessListener(Long id);
/**
* 获得流程监听器
*
* @param id 编号
* @return 流程监听器
*/
BpmProcessListenerDO getProcessListener(Long id);
/**
* 获得流程监听器分页
*
* @param pageReqVO 分页查询
* @return 流程监听器分页
*/
PageResult<BpmProcessListenerDO> getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO);
}

View File

@ -1,102 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.definition;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessListenerDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessListenerMapper;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerType;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerValueType;
import jakarta.annotation.Resource;
import org.flowable.engine.delegate.JavaDelegate;
import org.flowable.engine.delegate.TaskListener;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
/**
* BPM 流程监听器 Service 实现类
*
* @author 芋道源码
*/
@Service
@Validated
public class BpmProcessListenerServiceImpl implements BpmProcessListenerService {
@Resource
private BpmProcessListenerMapper processListenerMapper;
@Override
public Long createProcessListener(BpmProcessListenerSaveReqVO createReqVO) {
// 校验
validateCreateProcessListenerValue(createReqVO);
// 插入
BpmProcessListenerDO processListener = BeanUtils.toBean(createReqVO, BpmProcessListenerDO.class);
processListenerMapper.insert(processListener);
return processListener.getId();
}
@Override
public void updateProcessListener(BpmProcessListenerSaveReqVO updateReqVO) {
// 校验存在
validateProcessListenerExists(updateReqVO.getId());
validateCreateProcessListenerValue(updateReqVO);
// 更新
BpmProcessListenerDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessListenerDO.class);
processListenerMapper.updateById(updateObj);
}
private void validateCreateProcessListenerValue(BpmProcessListenerSaveReqVO createReqVO) {
// class 类型
if (createReqVO.getValueType().equals(BpmProcessListenerValueType.CLASS.getType())) {
try {
Class<?> clazz = Class.forName(createReqVO.getValue());
if (createReqVO.getType().equals(BpmProcessListenerType.EXECUTION.getType())
&& !JavaDelegate.class.isAssignableFrom(clazz)) {
throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(),
JavaDelegate.class.getName());
} else if (createReqVO.getType().equals(BpmProcessListenerType.TASK.getType())
&& !TaskListener.class.isAssignableFrom(clazz)) {
throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(),
TaskListener.class.getName());
}
} catch (ClassNotFoundException e) {
throw exception(PROCESS_LISTENER_CLASS_NOT_FOUND, createReqVO.getValue());
}
return;
}
// 表达式
if (!StrUtil.startWith(createReqVO.getValue(), "${") || !StrUtil.endWith(createReqVO.getValue(), "}")) {
throw exception(PROCESS_LISTENER_EXPRESSION_INVALID, createReqVO.getValue());
}
}
@Override
public void deleteProcessListener(Long id) {
// 校验存在
validateProcessListenerExists(id);
// 删除
processListenerMapper.deleteById(id);
}
private void validateProcessListenerExists(Long id) {
if (processListenerMapper.selectById(id) == null) {
throw exception(PROCESS_LISTENER_NOT_EXISTS);
}
}
@Override
public BpmProcessListenerDO getProcessListener(Long id) {
return processListenerMapper.selectById(id);
}
@Override
public PageResult<BpmProcessListenerDO> getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO) {
return processListenerMapper.selectPage(pageReqVO);
}
}

View File

@ -1,34 +0,0 @@
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 java.util.Collection;
/**
* 流程抄送 Service 接口
*
* 现在是在审批的时候进行流程抄送
*/
public interface BpmProcessInstanceCopyService {
/**
* 流程实例的抄送
*
* @param userIds 抄送的用户编号
* @param taskId 流程任务编号
*/
void createProcessInstanceCopy(Collection<Long> userIds, String taskId);
/**
* 获得抄送的流程的分页
*
* @param userId 当前登录用户
* @param pageReqVO 分页请求
* @return 抄送的分页结果
*/
PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,
BpmProcessInstanceCopyPageReqVO pageReqVO);
}

View File

@ -1,83 +0,0 @@
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.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;
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/**
* 流程抄送 Service 实现类
*
* @author kyle
*/
@Service
@Validated
@Slf4j
public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService {
@Resource
private BpmProcessInstanceCopyMapper processInstanceCopyMapper;
@Resource
@Lazy // 延迟加载避免循环依赖
private BpmTaskService taskService;
@Resource
@Lazy // 延迟加载避免循环依赖
private BpmProcessInstanceService processInstanceService;
@Resource
@Lazy // 延迟加载避免循环依赖
private BpmProcessDefinitionService processDefinitionService;
@Override
public void createProcessInstanceCopy(Collection<Long> userIds, String taskId) {
// 1.1 校验任务存在
Task task = taskService.getTask(taskId);
if (ObjectUtil.isNull(task)) {
throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
}
// 1.2 校验流程实例存在
String processInstanceId = task.getProcessInstanceId();
ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
if (processInstance == null) {
throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
}
// 1.3 校验流程定义存在
ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
processInstance.getProcessDefinitionId());
if (processDefinition == null) {
throw exception(ErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS);
}
// 2. 创建抄送流程
List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
.setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
.setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
.setCategory(processDefinition.getCategory()).setTaskId(taskId).setTaskName(task.getName()));
processInstanceCopyMapper.insertBatch(copyList);
}
@Override
public PageResult<BpmProcessInstanceCopyDO> getProcessInstanceCopyPage(Long userId,
BpmProcessInstanceCopyPageReqVO pageReqVO) {
return processInstanceCopyMapper.selectPage(userId, pageReqVO);
}
}

View File

@ -1,93 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateUserStrategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* {@link BpmTaskCandidateInvoker} 的单元测试
*
* @author 芋道源码
*/
public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidateInvoker taskCandidateInvoker;
@Mock
private AdminUserApi adminUserApi;
@Spy
private BpmTaskCandidateStrategy strategy = new BpmTaskCandidateUserStrategy();
@Spy
private List<BpmTaskCandidateStrategy> strategyList = Collections.singletonList(strategy);
@Test
public void testCalculateUsers() {
// 准备参数
String param = "1,2";
DelegateExecution execution = mock(DelegateExecution.class);
// mock 方法DelegateExecution
UserTask userTask = mock(UserTask.class);
when(execution.getCurrentFlowElement()).thenReturn(userTask);
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)))
.thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString());
when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)))
.thenReturn(param);
// mock 方法adminUserApi
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
.put(user2.getId(), user2).build();
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
// 调用
Set<Long> results = taskCandidateInvoker.calculateUsers(execution);
// 断言
assertEquals(asSet(1L, 2L), results);
}
@Test
public void testRemoveDisableUsers() {
// 准备参数. 1L 可以找到2L 是禁用的3L 找不到
Set<Long> assigneeUserIds = asSet(1L, 2L, 3L);
// mock 方法
AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L)
.setStatus(CommonStatusEnum.ENABLE.getStatus()));
AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L)
.setStatus(CommonStatusEnum.DISABLE.getStatus()));
Map<Long, AdminUserRespDTO> userMap = MapUtil.builder(user1.getId(), user1)
.put(user2.getId(), user2).build();
when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap);
// 调用
taskCandidateInvoker.removeDisableUsers(assigneeUserIds);
// 断言
assertEquals(asSet(1L), assigneeUserIds);
}
}

View File

@ -1,42 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidateDeptLeaderStrategy strategy;
@Mock
private DeptApi deptApi;
@Test
public void testCalculateUsers() {
// 准备参数
String param = "1,2";
// mock 方法
DeptRespDTO dept1 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(11L));
DeptRespDTO dept2 = randomPojo(DeptRespDTO.class, o -> o.setLeaderUserId(22L));
when(deptApi.getDeptList(eq(asSet(1L, 2L)))).thenReturn(asList(dept1, dept2));
// 调用
Set<Long> results = strategy.calculateUsers(null, param);
// 断言
assertEquals(asSet(11L, 22L), results);
}
}

View File

@ -1,42 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidateDeptMemberStrategy strategy;
@Mock
private AdminUserApi adminUserApi;
@Test
public void testCalculateUsers() {
// 准备参数
String param = "11,22";
// mock 方法
List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
id -> new AdminUserRespDTO().setId(id));
when(adminUserApi.getUserListByDeptIds(eq(asSet(11L, 22L)))).thenReturn(users);
// 调用
Set<Long> results = strategy.calculateUsers(null, param);
// 断言
assertEquals(asSet(11L, 22L), results);
}
}

View File

@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import org.flowable.engine.delegate.DelegateExecution;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.MockedStatic;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidateExpressionStrategy strategy;
@Test
public void testCalculateUsers() {
try (MockedStatic<FlowableUtils> flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) {
// 准备参数
String param = "1,2";
DelegateExecution execution = mock(DelegateExecution.class);
// mock 方法
flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(execution), eq(param)))
.thenReturn(asSet(1L, 2L));
// 调用
Set<Long> results = strategy.calculateUsers(execution, param);
// 断言
assertEquals(asSet(1L, 2L), results);
}
}
}

View File

@ -1,42 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.Arrays;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidateGroupStrategy strategy;
@Mock
private BpmUserGroupService userGroupService;
@Test
public void testCalculateUsers() {
// 准备参数
String param = "1,2";
// mock 方法
BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(11L, 12L)));
BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(21L, 22L)));
when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2));
// 调用
Set<Long> results = strategy.calculateUsers(null, param);
// 断言
assertEquals(asSet(11L, 12L, 21L, 22L), results);
}
}

View File

@ -1,45 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.List;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidatePostStrategy strategy;
@Mock
private PostApi postApi;
@Mock
private AdminUserApi adminUserApi;
@Test
public void testCalculateUsers() {
// 准备参数
String param = "1,2";
// mock 方法
List<AdminUserRespDTO> users = convertList(asSet(11L, 22L),
id -> new AdminUserRespDTO().setId(id));
when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(users);
// 调用
Set<Long> results = strategy.calculateUsers(null, param);
// 断言
assertEquals(asSet(11L, 22L), results);
}
}

View File

@ -1,41 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.RoleApi;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidateRoleStrategy strategy;
@Mock
private RoleApi roleApi;
@Mock
private PermissionApi permissionApi;
@Test
public void testCalculateUsers() {
// 准备参数
String param = "1,2";
// mock 方法
when(permissionApi.getUserRoleIdListByRoleIds(eq(asSet(1L, 2L))))
.thenReturn(asSet(11L, 22L));
// 调用
Set<Long> results = strategy.calculateUsers(null, param);
// 断言
assertEquals(asSet(11L, 22L), results);
}
}

View File

@ -1,28 +0,0 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest {
@InjectMocks
private BpmTaskCandidateUserStrategy strategy;
@Test
public void testCalculateUsers() {
// 准备参数
String param = "1,2";
// 调用
Set<Long> results = strategy.calculateUsers(null, param);
// 断言
assertEquals(asSet(1L, 2L), results);
}
}

View File

@ -1,136 +0,0 @@
package cn.iocoder.yudao.module.bpm.service.category;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link BpmCategoryServiceImpl} 的单元测试类
*
* @author 芋道源码
*/
@Import(BpmCategoryServiceImpl.class)
public class BpmCategoryServiceImplTest extends BaseDbUnitTest {
@Resource
private BpmCategoryServiceImpl categoryService;
@Resource
private BpmCategoryMapper categoryMapper;
@Test
public void testCreateCategory_success() {
// 准备参数
BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null)
.setStatus(randomCommonStatus());
// 调用
Long categoryId = categoryService.createCategory(createReqVO);
// 断言
assertNotNull(categoryId);
// 校验记录的属性是否正确
BpmCategoryDO category = categoryMapper.selectById(categoryId);
assertPojoEquals(createReqVO, category, "id");
}
@Test
public void testUpdateCategory_success() {
// mock 数据
BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class);
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
// 准备参数
BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> {
o.setId(dbCategory.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// 调用
categoryService.updateCategory(updateReqVO);
// 校验是否更新正确
BpmCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的
assertPojoEquals(updateReqVO, category);
}
@Test
public void testUpdateCategory_notExists() {
// 准备参数
BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS);
}
@Test
public void testDeleteCategory_success() {
// mock 数据
BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class);
categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbCategory.getId();
// 调用
categoryService.deleteCategory(id);
// 校验数据不存在了
assertNull(categoryMapper.selectById(id));
}
@Test
public void testDeleteCategory_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS);
}
@Test
public void testGetCategoryPage() {
// mock 数据
BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到
o.setName("芋头");
o.setCode("xiaodun");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2023, 2, 2));
});
categoryMapper.insert(dbCategory);
// 测试 name 不匹配
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾")));
// 测试 code 不匹配
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou")));
// 测试 status 不匹配
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2))));
// 准备参数
BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO();
reqVO.setName("");
reqVO.setCode("xiao");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
PageResult<BpmCategoryDO> pageResult = categoryService.getCategoryPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbCategory, pageResult.getList().get(0));
}
}