# Conflicts:
#	pom.xml
#	yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
#	yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java
#	yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPerformanceController.java
#	yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/performance/CrmStatisticsPerformanceReqVO.java
#	yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java
#	yudao-module-mall/yudao-module-promotion/pom.xml
#	yudao-module-mall/yudao-module-trade/pom.xml
#	yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java
#	yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java
#	yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
This commit is contained in:
YunaiV
2025-05-17 14:14:23 +08:00
3434 changed files with 1881 additions and 2639 deletions

View File

@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.bpm.api.event;
import lombok.Data;
import org.springframework.context.ApplicationEvent;
import javax.validation.constraints.NotNull;
/**
* 流程实例的状态(结果)发生变化的 Event
*
* @author 芋道源码
*/
@SuppressWarnings("ALL")
@Data
public class BpmProcessInstanceStatusEvent extends ApplicationEvent {
/**
* 流程实例的编号
*/
@NotNull(message = "流程实例的编号不能为空")
private String id;
/**
* 流程实例的 key
*/
@NotNull(message = "流程实例的 key 不能为空")
private String processDefinitionKey;
/**
* 流程实例的结果
*/
@NotNull(message = "流程实例的状态不能为空")
private Integer status;
/**
* 流程实例对应的业务标识
* 例如说,请假
*/
private String businessKey;
public BpmProcessInstanceStatusEvent(Object source) {
super(source);
}
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.hutool.core.util.StrUtil;
import org.springframework.context.ApplicationListener;
/**
* {@link BpmProcessInstanceStatusEvent} 的监听器
*
* @author 芋道源码
*/
public abstract class BpmProcessInstanceStatusEventListener
implements ApplicationListener<BpmProcessInstanceStatusEvent> {
@Override
public final void onApplicationEvent(BpmProcessInstanceStatusEvent event) {
if (!StrUtil.equals(event.getProcessDefinitionKey(), getProcessDefinitionKey())) {
return;
}
onEvent(event);
}
/**
* @return 返回监听的流程定义 Key
*/
protected abstract String getProcessDefinitionKey();
/**
* 处理事件
*
* @param event 事件
*/
protected abstract void onEvent(BpmProcessInstanceStatusEvent event);
}

View File

@@ -0,0 +1,4 @@
/**
* bpm API 包,定义并实现提供给其它模块的 API
*/
package cn.iocoder.yudao.module.bpm.api;

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.api.task;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import javax.validation.Valid;
/**
* 流程实例 Api 接口
*
* @author 芋道源码
*/
public interface BpmProcessInstanceApi {
/**
* 创建流程实例(提供给内部)
*
* @param userId 用户编号
* @param reqDTO 创建信息
* @return 实例的编号
*/
String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO);
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.bpm.api.task;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* Flowable 流程实例 Api 实现类
*
* @author 芋道源码
* @author jason
*/
@Service
@Validated
public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi {
@Resource
private BpmProcessInstanceService processInstanceService;
@Override
public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) {
return processInstanceService.createProcessInstance(userId, reqDTO);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.bpm.api.task;
import javax.validation.constraints.NotEmpty;
/**
* 流程任务 Api 接口
*
* @author jason
*/
public interface BpmProcessTaskApi {
/**
* 触发流程任务的执行
*
* @param processInstanceId 流程实例编号
* @param taskDefineKey 任务 Key
*/
void triggerTask(@NotEmpty(message = "流程实例的编号不能为空") String processInstanceId,
@NotEmpty(message = "任务 Key 不能为空") String taskDefineKey);
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.bpm.api.task;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 流程任务 Api 实现类
*
* @author jason
*/
@Service
@Validated
public class BpmProcessTaskApiImpl implements BpmProcessTaskApi {
@Resource
private BpmTaskService bpmTaskService;
@Override
public void triggerTask(String processInstanceId, String taskDefineKey) {
bpmTaskService.triggerTask(processInstanceId, taskDefineKey);
}
}

View File

@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.bpm.api.task.dto;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
/**
* 流程实例的创建 Request DTO
*
* @author 芋道源码
*/
@Data
public class BpmProcessInstanceCreateReqDTO {
/**
* 流程定义的标识
*/
@NotEmpty(message = "流程定义的标识不能为空")
private String processDefinitionKey;
/**
* 变量实例(动态表单)
*/
private Map<String, Object> variables;
/**
* 业务的唯一标识
*
* 例如说,请假申请的编号。通过它,可以查询到对应的实例
*/
@NotEmpty(message = "业务的唯一标识")
private String businessKey;
/**
* 发起人自选审批人 Map
*
* keytaskKey 任务编码
* value审批人的数组
* 例如:{ taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批
*/
private Map<String, List<Long>> startUserSelectAssignees;
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.bpm.controller.admin.base.dept;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "部门精简信息 VO")
@Data
public class DeptSimpleBaseVO {
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术部")
private String name;
}

View File

@@ -0,0 +1,4 @@
/**
* 基础包,放一些通用的 VO 类
*/
package cn.iocoder.yudao.module.bpm.controller.admin.base;

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.bpm.controller.admin.base.user;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户精简信息 VO")
@Data
public class UserSimpleBaseVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long id;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
private String nickname;
@Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png")
private String avatar;
@Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long deptId;
@Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部")
private String deptName;
}

View File

@@ -0,0 +1,95 @@
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 org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
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);
}
@PutMapping("/update-sort-batch")
@Operation(summary = "批量更新流程分类的排序")
@Parameter(name = "ids", description = "分类编号列表", required = true, example = "1,2,3")
@PreAuthorize("@ss.hasPermission('bpm:category:update')")
public CommonResult<Boolean> updateCategorySortBatch(@RequestParam("ids") List<Long> ids) {
categoryService.updateCategorySortBatch(ids);
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

@@ -0,0 +1,83 @@
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.form.BpmFormPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
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 = "管理后台 - 动态表单")
@RestController
@RequestMapping("/bpm/form")
@Validated
public class BpmFormController {
@Resource
private BpmFormService formService;
@PostMapping("/create")
@Operation(summary = "创建动态表单")
@PreAuthorize("@ss.hasPermission('bpm:form:create')")
public CommonResult<Long> createForm(@Valid @RequestBody BpmFormSaveReqVO createReqVO) {
return success(formService.createForm(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新动态表单")
@PreAuthorize("@ss.hasPermission('bpm:form:update')")
public CommonResult<Boolean> updateForm(@Valid @RequestBody BpmFormSaveReqVO updateReqVO) {
formService.updateForm(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除动态表单")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:form:delete')")
public CommonResult<Boolean> deleteForm(@RequestParam("id") Long id) {
formService.deleteForm(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得动态表单")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:form:query')")
public CommonResult<BpmFormRespVO> getForm(@RequestParam("id") Long id) {
BpmFormDO form = formService.getForm(id);
return success(BeanUtils.toBean(form, BpmFormRespVO.class));
}
@GetMapping({"/list-all-simple", "/simple-list"})
@Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框")
public CommonResult<List<BpmFormRespVO>> getFormSimpleList() {
List<BpmFormDO> list = formService.getFormList();
return success(convertList(list, formDO -> // 只返回 id、name 字段
new BpmFormRespVO().setId(formDO.getId()).setName(formDO.getName())));
}
@GetMapping("/page")
@Operation(summary = "获得动态表单分页")
@PreAuthorize("@ss.hasPermission('bpm:form:query')")
public CommonResult<PageResult<BpmFormRespVO>> getFormPage(@Valid BpmFormPageReqVO pageVO) {
PageResult<BpmFormDO> pageResult = formService.getFormPage(pageVO);
return success(BeanUtils.toBean(pageResult, BpmFormRespVO.class));
}
}

View File

@@ -0,0 +1,200 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO;
import cn.iocoder.yudao.module.bpm.convert.definition.BpmModelConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程模型")
@RestController
@RequestMapping("/bpm/model")
@Validated
public class BpmModelController {
@Resource
private BpmModelService modelService;
@Resource
private BpmFormService formService;
@Resource
private BpmCategoryService categoryService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@GetMapping("/list")
@Operation(summary = "获得模型分页")
@Parameter(name = "name", description = "模型名称", example = "芋艿")
public CommonResult<List<BpmModelRespVO>> getModelList(@RequestParam(value = "name", required = false) String name) {
List<Model> list = modelService.getModelList(name);
if (CollUtil.isEmpty(list)) {
return success(Collections.emptyList());
}
// 获得 Form 表单
Set<Long> formIds = convertSet(list, model -> {
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
return metaInfo != null ? metaInfo.getFormId() : null;
});
Map<Long, BpmFormDO> formMap = formService.getFormMap(formIds);
// 获得 Category Map
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
convertSet(list, Model::getCategory));
// 获得 Deployment Map
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
convertSet(list, Model::getDeploymentId));
// 获得 ProcessDefinition Map
List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds(
deploymentMap.keySet());
Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId);
// 获得 User Map、Dept Map
Set<Long> userIds = convertSetByFlatMap(list, model -> {
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty();
});
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Set<Long> deptIds = convertSetByFlatMap(list, model -> {
BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model);
return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty();
});
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds);
return success(BpmModelConvert.INSTANCE.buildModelList(list,
formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap));
}
@GetMapping("/get")
@Operation(summary = "获得模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:query')")
public CommonResult<BpmModelRespVO> getModel(@RequestParam("id") String id) {
Model model = modelService.getModel(id);
if (model == null) {
return null;
}
byte[] bpmnBytes = modelService.getModelBpmnXML(id);
BpmSimpleModelNodeVO simpleModel = modelService.getSimpleModel(id);
return success(BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes, simpleModel));
}
@PostMapping("/create")
@Operation(summary = "新建模型")
@PreAuthorize("@ss.hasPermission('bpm:model:create')")
public CommonResult<String> createModel(@Valid @RequestBody BpmModelSaveReqVO createRetVO) {
return success(modelService.createModel(createRetVO));
}
@PutMapping("/update")
@Operation(summary = "修改模型")
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
public CommonResult<Boolean> updateModel(@Valid @RequestBody BpmModelSaveReqVO modelVO) {
modelService.updateModel(getLoginUserId(), modelVO);
return success(true);
}
@PutMapping("/update-sort-batch")
@Operation(summary = "批量修改模型排序")
@Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3")
public CommonResult<Boolean> updateModelSortBatch(@RequestParam("ids") List<String> ids) {
modelService.updateModelSortBatch(getLoginUserId(), ids);
return success(true);
}
@PostMapping("/deploy")
@Operation(summary = "部署模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:deploy')")
public CommonResult<Boolean> deployModel(@RequestParam("id") String id) {
modelService.deployModel(getLoginUserId(), id);
return success(true);
}
@PutMapping("/update-state")
@Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态")
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
public CommonResult<Boolean> updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) {
modelService.updateModelState(getLoginUserId(), reqVO.getId(), reqVO.getState());
return success(true);
}
@Deprecated
@PutMapping("/update-bpmn")
@Operation(summary = "修改模型的 BPMN")
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
public CommonResult<Boolean> updateModelBpmn(@Valid @RequestBody BpmModeUpdateBpmnReqVO reqVO) {
modelService.updateModelBpmnXml(reqVO.getId(), reqVO.getBpmnXml());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:delete')")
public CommonResult<Boolean> deleteModel(@RequestParam("id") String id) {
modelService.deleteModel(getLoginUserId(), id);
return success(true);
}
@DeleteMapping("/clean")
@Operation(summary = "清理模型")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:model:clean')")
public CommonResult<Boolean> cleanModel(@RequestParam("id") String id) {
modelService.cleanModel(getLoginUserId(), id);
return success(true);
}
// ========== 仿钉钉/飞书的精简模型 =========
@GetMapping("/simple/get")
@Operation(summary = "获得仿钉钉流程设计模型")
@Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a")
public CommonResult<BpmSimpleModelNodeVO> getSimpleModel(@RequestParam("id") String modelId){
return success(modelService.getSimpleModel(modelId));
}
@Deprecated
@PostMapping("/simple/update")
@Operation(summary = "保存仿钉钉流程设计模型")
@PreAuthorize("@ss.hasPermission('bpm:model:update')")
public CommonResult<Boolean> updateSimpleModel(@Valid @RequestBody BpmSimpleModelUpdateReqVO reqVO) {
modelService.updateSimpleModel(getLoginUserId(), reqVO);
return success(Boolean.TRUE);
}
}

View File

@@ -0,0 +1,133 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程定义")
@RestController
@RequestMapping("/bpm/process-definition")
@Validated
public class BpmProcessDefinitionController {
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private BpmFormService formService;
@Resource
private BpmCategoryService categoryService;
@GetMapping("/page")
@Operation(summary = "获得流程定义分页")
@PreAuthorize("@ss.hasPermission('bpm:process-definition:query')")
public CommonResult<PageResult<BpmProcessDefinitionRespVO>> getProcessDefinitionPage(
BpmProcessDefinitionPageReqVO pageReqVO) {
PageResult<ProcessDefinition> pageResult = processDefinitionService.getProcessDefinitionPage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty(pageResult.getTotal()));
}
// 获得 Category Map
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
convertSet(pageResult.getList(), ProcessDefinition::getCategory));
// 获得 Deployment Map
Map<String, Deployment> deploymentMap = processDefinitionService.getDeploymentMap(
convertSet(pageResult.getList(), ProcessDefinition::getDeploymentId));
// 获得 BpmProcessDefinitionInfoDO Map
Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), ProcessDefinition::getId));
// 获得 Form Map
Map<Long, BpmFormDO> formMap = formService.getFormMap(
convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfoDO::getFormId));
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage(
pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap));
}
@GetMapping ("/list")
@Operation(summary = "获得流程定义列表")
@Parameter(name = "suspensionState", description = "挂起状态", required = true, example = "1") // 参见 Flowable SuspensionState 枚举
public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
@RequestParam("suspensionState") Integer suspensionState) {
// 1.1 获得开启的流程定义
List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState);
if (CollUtil.isEmpty(list)) {
return success(Collections.emptyList());
}
// 1.2 移除不可见的流程定义
Map<String, BpmProcessDefinitionInfoDO> processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(list, ProcessDefinition::getId));
Long userId = getLoginUserId();
list.removeIf(processDefinition -> {
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionMap.get(processDefinition.getId());
return processDefinitionInfo == null // 不存在
|| Boolean.FALSE.equals(processDefinitionInfo.getVisible()) // visible 不可见
|| !processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId); // 无权限发起
});
// 2. 拼接 VO 返回
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList(
list, null, processDefinitionMap, null, null));
}
@GetMapping("/simple-list")
@Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项")
public CommonResult<List<BpmProcessDefinitionRespVO>> getSimpleProcessDefinitionList() {
// 只查询未挂起的流程
List<ProcessDefinition> list = processDefinitionService.getProcessDefinitionListBySuspensionState(
SuspensionState.ACTIVE.getStateCode());
// 拼接 VO 返回,只返回 id、name、key
return success(convertList(list, definition -> new BpmProcessDefinitionRespVO()
.setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey())));
}
@GetMapping ("/get")
@Operation(summary = "获得流程定义")
@Parameter(name = "id", description = "流程编号", required = true, example = "1024")
@Parameter(name = "key", description = "流程定义标识", required = true, example = "1024")
public CommonResult<BpmProcessDefinitionRespVO> getProcessDefinition(
@RequestParam(value = "id", required = false) String id,
@RequestParam(value = "key", required = false) String key) {
ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id)
: processDefinitionService.getActiveProcessDefinition(key);
if (processDefinition == null) {
return success(null);
}
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId());
BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId());
return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
processDefinition, null, processDefinitionInfo, null, null, bpmnModel));
}
}

View File

@@ -0,0 +1,74 @@
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 org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
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

@@ -0,0 +1,74 @@
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 org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
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

@@ -0,0 +1,83 @@
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.group.BpmUserGroupPageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmUserGroupService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
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 = "管理后台 - 用户组")
@RestController
@RequestMapping("/bpm/user-group")
@Validated
public class BpmUserGroupController {
@Resource
private BpmUserGroupService userGroupService;
@PostMapping("/create")
@Operation(summary = "创建用户组")
@PreAuthorize("@ss.hasPermission('bpm:user-group:create')")
public CommonResult<Long> createUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO createReqVO) {
return success(userGroupService.createUserGroup(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新用户组")
@PreAuthorize("@ss.hasPermission('bpm:user-group:update')")
public CommonResult<Boolean> updateUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO updateReqVO) {
userGroupService.updateUserGroup(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除用户组")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:user-group:delete')")
public CommonResult<Boolean> deleteUserGroup(@RequestParam("id") Long id) {
userGroupService.deleteUserGroup(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得用户组")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('bpm:user-group:query')")
public CommonResult<BpmUserGroupRespVO> getUserGroup(@RequestParam("id") Long id) {
BpmUserGroupDO userGroup = userGroupService.getUserGroup(id);
return success(BeanUtils.toBean(userGroup, BpmUserGroupRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得用户组分页")
@PreAuthorize("@ss.hasPermission('bpm:user-group:query')")
public CommonResult<PageResult<BpmUserGroupRespVO>> getUserGroupPage(@Valid BpmUserGroupPageReqVO pageVO) {
PageResult<BpmUserGroupDO> pageResult = userGroupService.getUserGroupPage(pageVO);
return success(BeanUtils.toBean(pageResult, BpmUserGroupRespVO.class));
}
@GetMapping("/simple-list")
@Operation(summary = "获取用户组精简信息列表", description = "只包含被开启的用户组,主要用于前端的下拉选项")
public CommonResult<List<BpmUserGroupRespVO>> getUserGroupSimpleList() {
List<BpmUserGroupDO> list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(list, group -> new BpmUserGroupRespVO().setId(group.getId()).setName(group.getName())));
}
}

View File

@@ -0,0 +1,34 @@
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

@@ -0,0 +1,33 @@
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

@@ -0,0 +1,38 @@
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 lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@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

@@ -0,0 +1,33 @@
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

@@ -0,0 +1,30 @@
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

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@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

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import lombok.Data;
/**
* 流程表单字段 VO
*/
@Data
public class BpmFormFieldVO {
/**
* 字段类型
*/
private String type;
/**
* 字段标识
*/
private String field;
/**
* 字段标题
*/
private String title;
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 动态表单分页 Request VO")
@Data
public class BpmFormPageReqVO extends PageParam {
@Schema(description = "表单名称", example = "芋道")
private String name;
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 动态表单 Response VO")
@Data
public class BpmFormRespVO {
@Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, 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 = "表单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "表单状态不能为空")
private Integer status; // 参见 CommonStatusEnum 枚举
@Schema(description = "备注", example = "我是备注")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.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

@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
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;
@Schema(description = "管理后台 - 用户组分页 Request VO")
@Data
public class BpmUserGroupPageReqVO extends PageParam {
@Schema(description = "编号", example = "1024")
private Long id;
@Schema(description = "组名", example = "芋道")
private String name;
@Schema(description = "状态", example = "1")
private Integer status;
@DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "创建时间")
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Set;
@Schema(description = "管理后台 - 用户组 Response VO")
@Data
public class BpmUserGroupRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
private String description;
@Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
private Set<Long> userIds;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.Set;
@Schema(description = "管理后台 - 用户组创建/修改 Request VO")
@Data
public class BpmUserGroupSaveReqVO {
@Schema(description = "编号", example = "1024")
private Long id;
@Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
@NotNull(message = "组名不能为空")
private String name;
@Schema(description = "描述", example = "芋道源码")
private String description;
@Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
@NotNull(message = "成员编号数组不能为空")
private Set<Long> userIds;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer status;
}

View File

@@ -0,0 +1,30 @@
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

@@ -0,0 +1,36 @@
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

@@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.listener;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@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

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 流程模型的更新 BPMN XML Request VO")
@Data
public class BpmModeUpdateBpmnReqVO {
@Schema(description = "流程编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "流程编号不能为空")
private String id;
@Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "BPMN XML 不能为空")
private String bpmnXml;
}

View File

@@ -0,0 +1,180 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* BPM 流程 MetaInfo Response DTO
* 主要用于 { Model#setMetaInfo(String)} 的存储
*
* 最终,它的字段和
* {@link cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO}
* 是一致的
*
* @author 芋道源码
*/
@Data
public class BpmModelMetaInfoVO {
@Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg")
@URL(message = "流程图标格式不正确")
private String icon;
@Schema(description = "流程描述", example = "我是描述")
private String description;
@Schema(description = "流程类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(BpmModelTypeEnum.class)
@NotNull(message = "流程类型不能为空")
private Integer type;
@Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@InEnum(BpmModelFormTypeEnum.class)
@NotNull(message = "表单类型不能为空")
private Integer formType;
@Schema(description = "表单编号", example = "1024")
private Long formId; // formType 为 NORMAL 使用,必须非空
@Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create")
private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空
@Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view")
private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空
@Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否可见不能为空")
private Boolean visible;
@Schema(description = "可发起用户编号数组", example = "[1,2,3]")
private List<Long> startUserIds;
@Schema(description = "可发起部门编号数组", example = "[2,4,6]")
private List<Long> startDeptIds;
@Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]")
@NotEmpty(message = "可管理用户编号数组不能为空")
private List<Long> managerUserIds;
@Schema(description = "排序", example = "1")
private Long sort; // 创建时,后端自动生成
@Schema(description = "允许撤销审批中的申请", example = "true")
private Boolean allowCancelRunningProcess;
@Schema(description = "流程 ID 规则", example = "{}")
private ProcessIdRule processIdRule;
@Schema(description = "自动去重类型", example = "1")
@InEnum(BpmAutoApproveTypeEnum.class)
private Integer autoApprovalType;
@Schema(description = "标题设置", example = "{}")
private TitleSetting titleSetting;
@Schema(description = "摘要设置", example = "{}")
private SummarySetting summarySetting;
@Schema(description = "流程前置通知设置", example = "{}")
private HttpRequestSetting processBeforeTriggerSetting;
@Schema(description = "流程后置通知设置", example = "{}")
private HttpRequestSetting processAfterTriggerSetting;
@Schema(description = "任务前置通知设置", example = "{}")
private HttpRequestSetting taskBeforeTriggerSetting;
@Schema(description = "任务后置通知设置", example = "{}")
private HttpRequestSetting taskAfterTriggerSetting;
@Schema(description = "流程 ID 规则")
@Data
@Valid
public static class ProcessIdRule {
@Schema(description = "是否启用", example = "false")
@NotNull(message = "是否启用不能为空")
private Boolean enable;
@Schema(description = "前缀", example = "XX")
private String prefix;
@Schema(description = "中缀", example = "20250120")
private String infix; // 精确到日、精确到时、精确到分、精确到秒
@Schema(description = "后缀", example = "YY")
private String postfix;
@Schema(description = "序列长度", example = "5")
@NotNull(message = "序列长度不能为空")
private Integer length;
}
@Schema(description = "标题设置")
@Data
@Valid
public static class TitleSetting {
@Schema(description = "是否自定义", example = "false")
@NotNull(message = "是否自定义不能为空")
private Boolean enable;
@Schema(description = "标题", example = "流程标题")
private String title;
}
@Schema(description = "摘要设置")
@Data
@Valid
public static class SummarySetting {
@Schema(description = "是否自定义", example = "false")
@NotNull(message = "是否自定义不能为空")
private Boolean enable;
@Schema(description = "摘要字段数组", example = "[]")
private List<String> summary;
}
@Schema(description = "http 请求通知设置", example = "{}")
@Data
public static class HttpRequestSetting {
@Schema(description = "请求路径", example = "http://127.0.0.1")
@NotEmpty(message = "请求 URL 不能为空")
@URL(message = "请求 URL 格式不正确")
private String url;
@Schema(description = "请求头参数设置", example = "[]")
@Valid
private List<BpmSimpleModelNodeVO.HttpRequestParam> header;
@Schema(description = "请求头参数设置", example = "[]")
@Valid
private List<BpmSimpleModelNodeVO.HttpRequestParam> body;
/**
* 请求返回处理设置,用于修改流程表单值
* <p>
* key表示要修改的流程表单字段名(name)
* value接口返回的字段名
*/
@Schema(description = "请求返回处理设置", example = "[]")
private List<KeyValue<String, String>> response;
}
}

View File

@@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import cn.iocoder.yudao.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 流程模型 Response VO")
@Data
public class BpmModelRespVO extends BpmModelMetaInfoVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_yudao")
private String key;
@Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg")
private String icon;
@Schema(description = "流程分类编号", example = "1")
private String category;
@Schema(description = "流程分类名字", example = "请假")
private String categoryName;
@Schema(description = "表单名字", example = "请假表单")
private String formName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "可发起的用户数组")
private List<UserSimpleBaseVO> startUsers;
@Schema(description = "可发起的部门数组")
private List<DeptSimpleBaseVO> startDepts;
@Schema(description = "BPMN XML")
private String bpmnXml;
@Schema(description = "仿钉钉流程设计模型对象")
private BpmSimpleModelNodeVO simpleModel;
/**
* 最新部署的流程定义
*/
private BpmProcessDefinitionRespVO processDefinition;
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 流程模型的保存 Request VO")
@Data
public class BpmModelSaveReqVO extends BpmModelMetaInfoVO {
@Schema(description = "编号", example = "1024")
private String id;
@Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_yudao")
@NotEmpty(message = "流程标识不能为空")
private String key;
@Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
@NotEmpty(message = "流程名称不能为空")
private String name;
@Schema(description = "流程分类", example = "1")
private String category;
@Schema(description = "BPMN XML")
private String bpmnXml;
@Schema(description = "仿钉钉流程设计模型对象")
@Valid
private BpmSimpleModelNodeVO simpleModel;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 流程模型更新状态 Request VO")
@Data
public class BpmModelUpdateStateReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "编号不能为空")
private String id;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "状态不能为空")
private Integer state; // 参见 Flowable SuspensionState 枚举
}

View File

@@ -0,0 +1,526 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.*;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.flowable.bpmn.model.IOParameter;
import org.hibernate.validator.constraints.URL;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO")
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BpmSimpleModelNodeVO {
@Schema(description = "模型节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent_1")
@NotEmpty(message = "模型节点编号不能为空")
private String id;
@Schema(description = "模型节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模型节点类型不能为空")
@InEnum(BpmSimpleModelNodeTypeEnum.class)
private Integer type;
@Schema(description = "模型节点名称", example = "领导审批")
private String name;
@Schema(description = "节点展示内容", example = "指定成员: 芋道源码")
private String showText;
@Schema(description = "子节点")
private BpmSimpleModelNodeVO childNode; // 补充说明:在该模型下,子节点有且仅有一个,不会有多个
@Schema(description = "候选人策略", example = "30")
@InEnum(BpmTaskCandidateStrategyEnum.class)
private Integer candidateStrategy; // 用于审批,抄送节点
@Schema(description = "候选人参数")
private String candidateParam; // 用于审批,抄送节点
@Schema(description = "审批节点类型", example = "1")
@InEnum(BpmUserTaskApproveTypeEnum.class)
private Integer approveType; // 用于审批节点
@Schema(description = "多人审批方式", example = "1")
@InEnum(BpmUserTaskApproveMethodEnum.class)
private Integer approveMethod; // 用于审批节点
@Schema(description = "通过比例", example = "100")
private Integer approveRatio; // 通过比例,当多人审批方式为:多人会签(按通过比例) 需要设置
@Schema(description = "表单权限", example = "[]")
private List<Map<String, String>> fieldsPermission;
@Schema(description = "操作按钮设置", example = "[]")
private List<OperationButtonSetting> buttonsSetting; // 用于审批节点
@Schema(description = "是否需要签名", example = "false")
private Boolean signEnable;
@Schema(description = "是否填写审批意见", example = "false")
private Boolean reasonRequire;
/**
* 审批节点拒绝处理
*/
private RejectHandler rejectHandler;
/**
* 审批节点超时处理
*/
private TimeoutHandler timeoutHandler;
@Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1")
@InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class)
private Integer assignStartUserHandlerType;
/**
* 空处理策略
*/
private AssignEmptyHandler assignEmptyHandler;
/**
* 创建任务监听器
*/
private ListenerHandler taskCreateListener;
/**
* 指派任务监听器
*/
private ListenerHandler taskAssignListener;
/**
* 完成任务监听器
*/
private ListenerHandler taskCompleteListener;
@Schema(description = "延迟器设置", example = "{}")
private DelaySetting delaySetting;
@Schema(description = "条件节点")
private List<BpmSimpleModelNodeVO> conditionNodes; // 补充说明:有且仅有条件、并行、包容分支会使用
/**
* 条件节点设置
*/
private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE
@Schema(description = "路由分支组", example = "[]")
private List<RouterSetting> routerGroups;
@Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true
@JsonIgnore
private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE
/**
* 触发器节点设置
*/
private TriggerSetting triggerSetting;
@Schema(description = "附加节点 Id", example = "UserTask_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true
@JsonIgnore
private String attachNodeId; // 目前用于触发器节点HTTP 回调)。需要 UserTask 和 ReceiveTask附加节点) 来完成
/**
* 子流程设置
*/
private ChildProcessSetting childProcessSetting;
@Schema(description = "任务监听器")
@Valid
@Data
public static class ListenerHandler {
@Schema(description = "是否开启任务监听器", example = "false")
@NotNull(message = "是否开启任务监听器不能为空")
private Boolean enable;
@Schema(description = "请求路径", example = "http://xxxxx")
private String path;
@Schema(description = "请求头", example = "[]")
private List<HttpRequestParam> header;
@Schema(description = "请求体", example = "[]")
private List<HttpRequestParam> body;
}
@Schema(description = "HTTP 请求参数设置")
@Data
public static class HttpRequestParam {
@Schema(description = "值类型", example = "1")
@InEnum(BpmHttpRequestParamTypeEnum.class)
@NotNull(message = "值类型不能为空")
private Integer type;
@Schema(description = "", example = "xxx")
@NotEmpty(message = "键不能为空")
private String key;
@Schema(description = "", example = "xxx")
@NotEmpty(message = "值不能为空")
private String value;
}
@Schema(description = "审批节点拒绝处理策略")
@Data
public static class RejectHandler {
@Schema(description = "拒绝处理类型", example = "1")
@InEnum(BpmUserTaskRejectHandlerTypeEnum.class)
private Integer type;
@Schema(description = "任务拒绝后驳回的节点 Id", example = "Activity_1")
private String returnNodeId;
}
@Schema(description = "审批节点超时处理策略")
@Valid
@Data
public static class TimeoutHandler {
@Schema(description = "是否开启超时处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否开启超时处理不能为空")
private Boolean enable;
@Schema(description = "任务超时未处理的行为", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "任务超时未处理的行为不能为空")
@InEnum(BpmUserTaskTimeoutHandlerTypeEnum.class)
private Integer type;
@Schema(description = "超时时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "PT6H")
@NotEmpty(message = "超时时间不能为空")
private String timeDuration;
@Schema(description = "最大提醒次数", example = "1")
private Integer maxRemindCount;
}
@Schema(description = "空处理策略")
@Data
@Valid
public static class AssignEmptyHandler {
@Schema(description = "空处理类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "空处理类型不能为空")
@InEnum(BpmUserTaskAssignEmptyHandlerTypeEnum.class)
private Integer type;
@Schema(description = "指定人员审批的用户编号数组", example = "1")
private List<Long> userIds;
}
@Schema(description = "操作按钮设置")
@Data
@Valid
public static class OperationButtonSetting {
// TODO @jason是不是按钮的标识id 会和数据库的 id 自增有点模糊key 标识会更合理一点点哈。
@Schema(description = "按钮 Id", example = "1")
private Integer id;
@Schema(description = "显示名称", example = "审批")
private String displayName;
@Schema(description = "是否启用", example = "true")
private Boolean enable;
}
@Schema(description = "条件设置")
@Data
@Valid
// 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE
public static class ConditionSetting {
@Schema(description = "条件类型", example = "1")
@InEnum(BpmSimpleModeConditionTypeEnum.class)
private Integer conditionType;
@Schema(description = "条件表达式", example = "${day>3}")
private String conditionExpression;
@Schema(description = "是否默认条件", example = "true")
private Boolean defaultFlow;
/**
* 条件组
*/
private ConditionGroups conditionGroups;
}
@Schema(description = "条件组")
@Data
@Valid
public static class ConditionGroups {
@Schema(description = "条件组下的条件关系是否为与关系", example = "true")
@NotNull(message = "条件关系不能为空")
private Boolean and;
@Schema(description = "条件组下的条件", example = "[]")
@NotEmpty(message = "条件不能为空")
private List<Condition> conditions;
}
@Schema(description = "条件")
@Data
@Valid
public static class Condition {
@Schema(description = "条件下的规则关系是否为与关系", example = "true")
@NotNull(message = "规则关系不能为空")
private Boolean and;
@Schema(description = "条件下的规则", example = "[]")
@NotEmpty(message = "规则不能为空")
private List<ConditionRule> rules;
}
@Schema(description = "条件规则")
@Data
@Valid
public static class ConditionRule {
@Schema(description = "运行符号", example = "==")
@NotEmpty(message = "运行符号不能为空")
private String opCode;
@Schema(description = "运算符左边的值,例如某个流程变量", example = "startUserId")
@NotEmpty(message = "运算符左边的值不能为空")
private String leftSide;
@Schema(description = "运算符右边的值", example = "1")
@NotEmpty(message = "运算符右边的值不能为空")
private String rightSide;
}
@Schema(description = "延迟器")
@Data
@Valid
public static class DelaySetting {
@Schema(description = "延迟时间类型", example = "1")
@NotNull(message = "延迟时间类型不能为空")
@InEnum(BpmDelayTimerTypeEnum.class)
private Integer delayType;
@Schema(description = "延迟时间表达式", example = "PT1H,2025-01-01T00:00:00")
@NotEmpty(message = "延迟时间表达式不能为空")
private String delayTime;
}
@Schema(description = "路由分支")
@Data
@Valid
public static class RouterSetting {
@Schema(description = "节点 Id", example = "Activity_xxx") // 跳转到该节点
@NotEmpty(message = "节点 Id 不能为空")
private String nodeId;
@Schema(description = "条件类型", example = "1")
@InEnum(BpmSimpleModeConditionTypeEnum.class)
@NotNull(message = "条件类型不能为空")
private Integer conditionType;
@Schema(description = "条件表达式", example = "${day>3}")
private String conditionExpression;
@Schema(description = "条件组", example = "{}")
private ConditionGroups conditionGroups;
}
@Schema(description = "触发器节点配置")
@Data
@Valid
public static class TriggerSetting {
@Schema(description = "触发器类型", example = "1")
@InEnum(BpmTriggerTypeEnum.class)
@NotNull(message = "触发器类型不能为空")
private Integer type;
/**
* http 请求触发器设置
*/
@Valid
private HttpRequestTriggerSetting httpRequestSetting;
/**
* 流程表单触发器设置
*/
private List<FormTriggerSetting> formSettings;
@Schema(description = "http 请求触发器设置", example = "{}")
@Data
public static class HttpRequestTriggerSetting {
@Schema(description = "请求路径", example = "http://127.0.0.1")
@NotEmpty(message = "请求 URL 不能为空")
@URL(message = "请求 URL 格式不正确")
private String url;
@Schema(description = "请求头参数设置", example = "[]")
@Valid
private List<HttpRequestParam> header;
@Schema(description = "请求头参数设置", example = "[]")
@Valid
private List<HttpRequestParam> body;
/**
* 请求返回处理设置,用于修改流程表单值
* <p>
* key表示要修改的流程表单字段名(name)
* value接口返回的字段名
*/
@Schema(description = "请求返回处理设置", example = "[]")
private List<KeyValue<String, String>> response;
/**
* Http 回调请求,需要指定回调任务 Key用于回调执行
*/
@Schema(description = "回调任务 Key", example = "xxx", hidden = true)
private String callbackTaskDefineKey;
}
@Schema(description = "流程表单触发器设置", example = "{}")
@Data
public static class FormTriggerSetting {
@Schema(description = "条件类型", example = "1")
@InEnum(BpmSimpleModeConditionTypeEnum.class)
private Integer conditionType;
@Schema(description = "条件表达式", example = "${day>3}")
private String conditionExpression;
@Schema(description = "条件组", example = "{}")
private ConditionGroups conditionGroups;
@Schema(description = "修改的表单字段", example = "{}")
private Map<String, Object> updateFormFields;
@Schema(description = "删除表单字段", example = "[]")
private Set<String> deleteFields;
}
}
@Schema(description = "子流程节点配置")
@Data
@Valid
public static class ChildProcessSetting {
@Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
@NotEmpty(message = "被调用流程不能为空")
private String calledProcessDefinitionKey;
@Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
@NotEmpty(message = "被调用流程名称不能为空")
private String calledProcessDefinitionName;
@Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否异步不能为空")
private Boolean async;
@Schema(description = "输入参数(主->子)", example = "[]")
private List<IOParameter> inVariables;
@Schema(description = "输出参数(子->主)", example = "[]")
private List<IOParameter> outVariables;
@Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否自动跳过子流程发起节点不能为空")
private Boolean skipStartUserNode;
@Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
@NotNull(message = "子流程发起人配置不能为空")
private StartUserSetting startUserSetting;
@Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
private TimeoutSetting timeoutSetting;
@Schema(description = "多实例设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
private MultiInstanceSetting multiInstanceSetting;
@Schema(description = "子流程发起人配置")
@Data
@Valid
public static class StartUserSetting {
@Schema(description = "子流程发起人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "子流程发起人类型")
@InEnum(BpmChildProcessStartUserTypeEnum.class)
private Integer type;
@Schema(description = "表单", example = "xxx")
private String formField;
@Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "当子流程发起人为空时类型不能为空")
@InEnum(BpmChildProcessStartUserEmptyTypeEnum.class)
private Integer emptyType;
}
@Schema(description = "超时设置")
@Data
@Valid
public static class TimeoutSetting {
@Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否开启超时设置不能为空")
private Boolean enable;
@Schema(description = "时间类型", example = "1")
@InEnum(BpmDelayTimerTypeEnum.class)
private Integer type;
@Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00")
private String timeExpression;
}
@Schema(description = "多实例设置")
@Data
@Valid
public static class MultiInstanceSetting {
@Schema(description = "是否开启多实例", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否开启多实例不能为空")
private Boolean enable;
@Schema(description = "是否串行", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否串行不能为空")
private Boolean sequential;
@Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
@NotNull(message = "完成比例不能为空")
private Integer approveRatio;
@Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "多实例来源类型不能为空")
@InEnum(BpmChildProcessMultiInstanceSourceTypeEnum.class)
private Integer sourceType;
@Schema(description = "多实例来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "多实例来源不能为空")
private String source;
}
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
// TODO @jason需要考虑如果某个节点的配置不正确需要有提示具体怎么实现可以讨论下
@Schema(description = "管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO")
@Data
public class BpmSimpleModelUpdateReqVO {
@Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotEmpty(message = "流程模型编号不能为空")
private String id; // 对应 Flowable act_re_model 表 ID_ 字段
@Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "仿钉钉流程设计模型对象不能为空")
@Valid
private BpmSimpleModelNodeVO simpleModel;
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Schema(description = "管理后台 - 流程定义分页 Request VO")
@Data
public class BpmProcessDefinitionPageReqVO extends PageParam {
@Schema(description = "标识-精准匹配", example = "process1641042089407")
private String key;
}

View File

@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 流程定义 Response VO")
@Data
public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer version;
@Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao")
private String key;
@Schema(description = "流程分类", example = "1")
private String category;
@Schema(description = "流程分类名字", example = "请假")
private String categoryName;
@Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer modelType; // 参见 BpmModelTypeEnum 枚举类
@Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
private String modelId;
@Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
private String formConf;
@Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> formFields;
@Schema(description = "表单名字", example = "请假表单")
private String formName;
@Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer suspensionState; // 参见 SuspensionState 枚举
@Schema(description = "部署时间")
private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回
@Schema(description = "BPMN XML")
private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回
@Schema(description = "SIMPLE 设计器模型数据 json 格式")
private String simpleModel; // 非必须返回
@Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long sort;
@Schema(description = "BPMN UserTask 用户任务")
@Data
public static class UserTask {
@Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "sudo")
private String id;
@Schema(description = "任务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
private String name;
}
}

View File

@@ -0,0 +1,12 @@
### 请求 /bpm/oa/leave/create 接口 => 成功
POST {{baseUrl}}/bpm/oa/leave/create
Content-Type: application/json
tenant-id: 1
Authorization: Bearer {{token}}
{
"startTime": "2022-03-01",
"endTime": "2022-03-05",
"type": 1,
"reason": "我要请假啦啦啦!"
}

View File

@@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa;
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.oa.vo.BpmOALeaveCreateReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;
import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
/**
* OA 请假申请 Controller用于演示自己存储数据接入工作流的例子
*
* @author jason
* @author 芋道源码
*/
@Tag(name = "管理后台 - OA 请假申请")
@RestController
@RequestMapping("/bpm/oa/leave")
@Validated
public class BpmOALeaveController {
@Resource
private BpmOALeaveService leaveService;
@PostMapping("/create")
@PreAuthorize("@ss.hasPermission('bpm:oa-leave:create')")
@Operation(summary = "创建请求申请")
public CommonResult<Long> createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) {
return success(leaveService.createLeave(getLoginUserId(), createReqVO));
}
@GetMapping("/get")
@PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')")
@Operation(summary = "获得请假申请")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
public CommonResult<BpmOALeaveRespVO> getLeave(@RequestParam("id") Long id) {
BpmOALeaveDO leave = leaveService.getLeave(id);
return success(BeanUtils.toBean(leave, BpmOALeaveRespVO.class));
}
@GetMapping("/page")
@PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')")
@Operation(summary = "获得请假申请分页")
public CommonResult<PageResult<BpmOALeaveRespVO>> getLeavePage(@Valid BpmOALeavePageReqVO pageVO) {
PageResult<BpmOALeaveDO> pageResult = leaveService.getLeavePage(getLoginUserId(), pageVO);
return success(BeanUtils.toBean(pageResult, BpmOALeaveRespVO.class));
}
}

View File

@@ -0,0 +1,5 @@
/**
* OA 示例,用于演示外部业务接入 BPM 工作流的示例
* 一般的接入方式,只需要调用 接口,后续 Admin 用户在管理后台的【待办事务】进行审批
*/
package cn.iocoder.yudao.module.bpm.controller.admin.oa;

View File

@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 请假申请创建 Request VO")
@Data
public class BpmOALeaveCreateReqVO {
@Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "开始时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
@Schema(description = "请假类型-参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type;
@Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码")
private String reason;
@Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}")
private Map<String, List<Long>> startUserSelectAssignees;
@AssertTrue(message = "结束时间,需要在开始时间之后")
public boolean isEndTimeValid() {
return !getEndTime().isBefore(getStartTime());
}
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
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 = "管理后台 - 请假申请分页 Request VO")
@Data
public class BpmOALeavePageReqVO extends PageParam {
@Schema(description = "状态", example = "1")
private Integer status; // 参见 BpmProcessInstanceResultEnum 枚举
@Schema(description = "请假类型,参见 bpm_oa_type", example = "1")
private Integer type;
@Schema(description = "原因,模糊匹配", example = "阅读芋道源码")
private String reason;
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@Schema(description = "申请时间")
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 请假申请 Response VO")
@Data
public class BpmOALeaveRespVO {
@Schema(description = "请假表单主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "请假类型,参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type;
@Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码")
private String reason;
@Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "流程编号")
private String processInstanceId;
@Schema(description = "审批结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
}

View File

@@ -0,0 +1,16 @@
### 请求 /bpm/process-instance/get-bpmn 接口 => 成功
GET {{baseUrl}}/bpm/process-instance/get-bpmn-model-view?id=1d5fb5a6-85f8-11ef-b717-7e93075f94e3
Content-Type: application/json
tenant-id: 1
Authorization: Bearer {{token}}
### 请求 /bpm/process-instance/get-bpmn 接口 => 失败
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=1d5fb5a6-85f8-11ef-b717-7e93075f94e3
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=3ee5c5ba-904a-11ef-a76e-b2ed5d6ef911
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=f630dfa2-8f92-11ef-947c-ba5e239a6eb4
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=9de8bdbf-9133-11ef-ae97-eaf49df1f932
#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=dd2188eb-9394-11ef-a039-7a9ac3d9eb6b
GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processDefinitionId=test-auto:1:c70a799a-9394-11ef-a039-7a9ac3d9eb6b
Content-Type: application/json
tenant-id: 1
Authorization: Bearer {{token}}

View File

@@ -0,0 +1,199 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请”
@RestController
@RequestMapping("/bpm/process-instance")
@Validated
public class BpmProcessInstanceController {
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private BpmCategoryService categoryService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@GetMapping("/my-page")
@Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<PageResult<BpmProcessInstanceRespVO>> getProcessInstanceMyPage(
@Valid BpmProcessInstancePageReqVO pageReqVO) {
PageResult<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage(
getLoginUserId(), pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty(pageResult.getTotal()));
}
// 拼接返回
Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(
convertList(pageResult.getList(), HistoricProcessInstance::getId));
Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap(
convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
Set<Long> userIds = convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId()));
userIds.addAll(convertSetByFlatMap(taskMap.values(),
tasks -> tasks.stream().map(Task::getAssignee).filter(StrUtil::isNotBlank).map(Long::parseLong)));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult,
processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap));
}
@GetMapping("/manager-page")
@Operation(summary = "获得管理流程实例的分页列表", description = "在【流程实例】菜单中,进行调用")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:manager-query')")
public CommonResult<PageResult<BpmProcessInstanceRespVO>> getProcessInstanceManagerPage(
@Valid BpmProcessInstancePageReqVO pageReqVO) {
PageResult<HistoricProcessInstance> pageResult = processInstanceService.getProcessInstancePage(
null, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty(pageResult.getTotal()));
}
// 拼接返回
Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(
convertList(pageResult.getList(), HistoricProcessInstance::getId));
Map<String, ProcessDefinition> processDefinitionMap = processDefinitionService.getProcessDefinitionMap(
convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
Map<String, BpmCategoryDO> categoryMap = categoryService.getCategoryMap(
convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory));
// 发起人信息
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId));
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult,
processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap));
}
@PostMapping("/create")
@Operation(summary = "新建流程实例")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
}
@GetMapping("/get")
@Operation(summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用")
@Parameter(name = "id", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
public CommonResult<BpmProcessInstanceRespVO> getProcessInstance(@RequestParam("id") String id) {
HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id);
if (processInstance == null) {
return success(null);
}
// 拼接返回
ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
processInstance.getProcessDefinitionId());
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(
processInstance.getProcessDefinitionId());
AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId()));
DeptRespDTO dept = null;
if (startUser != null && startUser.getDeptId() != null) {
dept = deptApi.getDept(startUser.getDeptId());
}
return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance,
processDefinition, processDefinitionInfo, startUser, dept));
}
@DeleteMapping("/cancel-by-start-user")
@Operation(summary = "用户取消流程实例", description = "取消发起的流程")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')")
public CommonResult<Boolean> cancelProcessInstanceByStartUser(
@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {
processInstanceService.cancelProcessInstanceByStartUser(getLoginUserId(), cancelReqVO);
return success(true);
}
@DeleteMapping("/cancel-by-admin")
@Operation(summary = "管理员取消流程实例", description = "管理员撤回流程")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel-by-admin')")
public CommonResult<Boolean> cancelProcessInstanceByManager(
@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {
processInstanceService.cancelProcessInstanceByAdmin(getLoginUserId(), cancelReqVO);
return success(true);
}
@GetMapping("/get-approval-detail")
@Operation(summary = "获得审批详情")
@Parameter(name = "id", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@SuppressWarnings("unchecked")
public CommonResult<BpmApprovalDetailRespVO> getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) {
if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) {
reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class));
}
return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO));
}
@GetMapping("/get-next-approval-nodes")
@Operation(summary = "获取下一个执行的流程节点")
@PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
@SuppressWarnings("unchecked")
public CommonResult<List<BpmApprovalDetailRespVO.ActivityNode>> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) {
if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) {
reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class));
}
return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO));
}
@GetMapping("/get-bpmn-model-view")
@Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用")
@Parameter(name = "id", description = "流程实例的编号", required = true)
public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) {
return success(processInstanceService.getProcessInstanceBpmnModelView(id));
}
}

View File

@@ -0,0 +1,89 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Map;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程实例抄送")
@RestController
@RequestMapping("/bpm/process-instance/copy")
@Validated
public class BpmProcessInstanceCopyController {
@Resource
private BpmProcessInstanceCopyService processInstanceCopyService;
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private AdminUserApi adminUserApi;
@GetMapping("/page")
@Operation(summary = "获得抄送流程分页列表")
@PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')")
public CommonResult<PageResult<BpmProcessInstanceCopyRespVO>> getProcessInstanceCopyPage(
@Valid BpmProcessInstanceCopyPageReqVO pageReqVO) {
PageResult<BpmProcessInstanceCopyDO> pageResult = processInstanceCopyService.getProcessInstanceCopyPage(
getLoginUserId(), pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(new PageResult<>(pageResult.getTotal()));
}
// 拼接返回
Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(),
copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator()))));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId));
return success(convertPage(pageResult, copy -> {
BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class);
MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()),
user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
MapUtils.findAndThen(userMap, copy.getStartUserId(),
user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class)));
MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(),
processInstance -> {
copyVO.setSummary(FlowableUtils.getSummary(
processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
processInstance.getProcessVariables()));
copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime()));
});
return copyVO;
}));
}
}

View File

@@ -0,0 +1,239 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 流程任务实例")
@RestController
@RequestMapping("/bpm/task")
@Validated
public class BpmTaskController {
@Resource
private BpmTaskService taskService;
@Resource
private BpmProcessInstanceService processInstanceService;
@Resource
private BpmFormService formService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@GetMapping("todo-page")
@Operation(summary = "获取 Todo 待办任务分页")
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<PageResult<BpmTaskRespVO>> getTaskTodoPage(@Valid BpmTaskPageReqVO pageVO) {
PageResult<Task> pageResult = taskService.getTaskTodoPage(getLoginUserId(), pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty());
}
// 拼接数据
Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(
convertSet(pageResult.getList(), Task::getProcessInstanceId));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), Task::getProcessDefinitionId));
return success(BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap));
}
@GetMapping("done-page")
@Operation(summary = "获取 Done 已办任务分页")
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<PageResult<BpmTaskRespVO>> getTaskDonePage(@Valid BpmTaskPageReqVO pageVO) {
PageResult<HistoricTaskInstance> pageResult = taskService.getTaskDonePage(getLoginUserId(), pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty());
}
// 拼接数据
Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId));
return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap));
}
@GetMapping("manager-page")
@Operation(summary = "获取全部任务的分页", description = "用于【流程任务】菜单")
@PreAuthorize("@ss.hasPermission('bpm:task:mananger-query')")
public CommonResult<PageResult<BpmTaskRespVO>> getTaskManagerPage(@Valid BpmTaskPageReqVO pageVO) {
PageResult<HistoricTaskInstance> pageResult = taskService.getTaskPage(getLoginUserId(), pageVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return success(PageResult.empty());
}
// 拼接数据
Map<String, HistoricProcessInstance> processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId));
// 获得 User 和 Dept Map
Set<Long> userIds = convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()));
userIds.addAll(convertSet(pageResult.getList(), task -> NumberUtils.parseLong(task.getAssignee())));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(
convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId));
return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap));
}
@GetMapping("/list-by-process-instance-id")
@Operation(summary = "获得指定流程实例的任务列表", description = "包括完成的、未完成的")
@Parameter(name = "processInstanceId", description = "流程实例的编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<List<BpmTaskRespVO>> getTaskListByProcessInstanceId(
@RequestParam("processInstanceId") String processInstanceId) {
List<HistoricTaskInstance> taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true);
if (CollUtil.isEmpty(taskList)) {
return success(Collections.emptyList());
}
// 拼接数据
Set<Long> userIds = convertSetByFlatMap(taskList, task ->
Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner())));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
// 获得 Form Map
Map<Long, BpmFormDO> formMap = formService.getFormMap(
convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));
return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList,
formMap, userMap, deptMap));
}
@PutMapping("/approve")
@Operation(summary = "通过任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
taskService.approveTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/reject")
@Operation(summary = "不通过任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
taskService.rejectTask(getLoginUserId(), reqVO);
return success(true);
}
@GetMapping("/list-by-return")
@Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮")
@Parameter(name = "taskId", description = "当前任务ID", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<List<BpmTaskRespVO>> getTaskListByReturn(@RequestParam("id") String id) {
List<UserTask> userTaskList = taskService.getUserTaskListByReturn(id);
return success(convertList(userTaskList, userTask -> // 只返回 id 和 name
new BpmTaskRespVO().setName(userTask.getName()).setTaskDefinitionKey(userTask.getId())));
}
@PutMapping("/return")
@Operation(summary = "退回任务", description = "用于【流程详情】的【退回】按钮")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) {
taskService.returnTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/delegate")
@Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) {
taskService.delegateTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/transfer")
@Operation(summary = "转派任务", description = "用于【流程详情】的【转派】按钮")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> transferTask(@Valid @RequestBody BpmTaskTransferReqVO reqVO) {
taskService.transferTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/create-sign")
@Operation(summary = "加签", description = "before 前加签after 后加签")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> createSignTask(@Valid @RequestBody BpmTaskSignCreateReqVO reqVO) {
taskService.createSignTask(getLoginUserId(), reqVO);
return success(true);
}
@DeleteMapping("/delete-sign")
@Operation(summary = "减签")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> deleteSignTask(@Valid @RequestBody BpmTaskSignDeleteReqVO reqVO) {
taskService.deleteSignTask(getLoginUserId(), reqVO);
return success(true);
}
@PutMapping("/copy")
@Operation(summary = "抄送任务")
@PreAuthorize("@ss.hasPermission('bpm:task:update')")
public CommonResult<Boolean> copyTask(@Valid @RequestBody BpmTaskCopyReqVO reqVO) {
taskService.copyTask(getLoginUserId(), reqVO);
return success(true);
}
@GetMapping("/list-by-parent-task-id")
@Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表
@Parameter(name = "parentTaskId", description = "父级任务编号", required = true)
@PreAuthorize("@ss.hasPermission('bpm:task:query')")
public CommonResult<List<BpmTaskRespVO>> getTaskListByParentTaskId(@RequestParam("parentTaskId") String parentTaskId) {
List<Task> taskList = taskService.getTaskListByParentTaskId(parentTaskId);
if (CollUtil.isEmpty(taskList)) {
return success(Collections.emptyList());
}
// 拼接数据
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList,
user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner()))));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
return success(BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap));
}
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 流程活动的 Response VO")
@Data
public class BpmActivityRespVO {
@Schema(description = "流程活动的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String key;
@Schema(description = "流程活动的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent")
private String type;
@Schema(description = "流程活动的开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "流程活动的结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "关联的流程任务的编号", example = "2048")
private String taskId; // 关联的流程任务,只有 UserTask 等类型才有
}

View File

@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@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)
private UserSimpleBaseVO startUser;
@Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233")
private String processInstanceId;
@Schema(description = "流程实例的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试")
private String processInstanceName;
@Schema(description = "流程实例的发起时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime processInstanceStartTime;
@Schema(description = "流程活动的编号", requiredMode = Schema.RequiredMode.REQUIRED)
private String activityId;
@Schema(description = "流程活动的名字", requiredMode = Schema.RequiredMode.REQUIRED)
private String activityName;
@Schema(description = "流程活动的编号")
private String taskId;
@Schema(description = "抄送人意见")
private String reason;
@Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED)
private UserSimpleBaseVO createUser;
@Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "流程摘要", example = "[]")
private List<KeyValue<String, String>> summary;
}

View File

@@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.AssertTrue;
import java.util.Map;
@Schema(description = "管理后台 - 审批详情 Request VO")
@Data
public class BpmApprovalDetailReqVO {
@Schema(description = "流程定义的编号", example = "1024")
private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID
@Schema(description = "流程变量")
private Map<String, Object> processVariables; // 使用场景:同 processDefinitionId用于流程预测
@Schema(description = "流程变量")
private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量
@Schema(description = "流程实例的编号", example = "1024")
private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID
// TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。
@Schema(description = "流程活动编号", example = "StartUserNode")
private String activityId; // 用于获取表单权限。1发起流程时传“发起人节点” activityId 可获取发起人的表单权限2从抄送列表界面进来时传抄送的 activityId 可获取抄送人的表单权限;
@Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b")
private String taskId; // 用于获取表单权限。1从待审批/已审批界面进来时,传递 taskId 任务编号,可获取任务节点的变得权限
@AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空")
@JsonIgnore
public boolean isValidProcessParam() {
return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId);
}
}

View File

@@ -0,0 +1,112 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 审批详情 Response VO")
@Data
public class BpmApprovalDetailRespVO {
@Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
@Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<ActivityNode> activityNodes;
@Schema(description = "表单字段权限")
private Map<String, String> formFieldsPermission;
@Schema(description = "待办任务")
private BpmTaskRespVO todoTask;
/**
* 所属流程定义信息
*/
private BpmProcessDefinitionRespVO processDefinition;
/**
* 所属流程实例信息
*/
private BpmProcessInstanceRespVO processInstance;
@Schema(description = "活动节点信息")
@Data
public static class ActivityNode {
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode")
private String id;
@Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人")
private String name;
@Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举
@Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status; // 参见 BpmTaskStatusEnum 枚举
@Schema(description = "节点的开始时间")
private LocalDateTime startTime;
@Schema(description = "节点的结束时间")
private LocalDateTime endTime;
@Schema(description = "审批节点的任务信息")
private List<ActivityNodeTask> tasks;
@Schema(description = "候选人策略", example = "35")
private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时,审批节点、抄送节点自选
@Schema(description = "候选人用户 ID 列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818")
@JsonIgnore // 不返回,只是方便后续读取,赋值给 candidateUsers
private List<Long> candidateUserIds;
@Schema(description = "候选人用户列表")
private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表
@Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf")
private String processInstanceId; // 当且仅当该节点是子流程节点时才会有值CallActivity 的 processInstanceId 字段)
}
@Schema(description = "活动节点的任务信息")
@Data
public static class ActivityNodeTask {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String id;
@Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818")
@JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser
private Long owner;
@Schema(description = "任务所属人", example = "1024")
private UserSimpleBaseVO ownerUser;
@Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
@JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser
private Long assignee;
@Schema(description = "任务分配人", example = "2048")
private UserSimpleBaseVO assigneeUser;
@Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 BpmTaskStatusEnum 枚举
@Schema(description = "审批意见", example = "同意")
private String reason;
@Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
private String signPicUrl;
}
}

View File

@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
import java.util.Set;
@Schema(description = "管理后台 - 流程示例的 BPMN 视图 Response VO")
@Data
public class BpmProcessInstanceBpmnModelViewRespVO {
// ========== 基本信息 ==========
@Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED)
private BpmProcessInstanceRespVO processInstance;
@Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<BpmTaskRespVO> tasks;
@Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED)
private String bpmnXml;
@Schema(description = "SIMPLE 模型")
private BpmSimpleModelNodeVO simpleModel;
// ========== 进度信息 ==========
@Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> unfinishedTaskActivityIds; // 只包括 UserTask
@Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> finishedTaskActivityIds; // 包括 UserTask、Gateway 等,不包括 SequenceFlow
@Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> finishedSequenceFlowActivityIds; // 只包括 SequenceFlow
@Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
private Set<String> rejectedTaskActivityIds; // 只包括 UserTask
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 流程实例的取消 Request VO")
@Data
public class BpmProcessInstanceCancelReqVO {
@Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "流程实例的编号不能为空")
private String id;
@Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "不请假了!")
@NotEmpty(message = "取消原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
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 = "管理后台 - 流程实例抄送的分页 Request VO")
@Data
public class BpmProcessInstanceCopyPageReqVO extends PageParam {
@Schema(description = "流程名称", example = "芋道")
private String processInstanceName;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 流程实例的创建 Request VO")
@Data
public class BpmProcessInstanceCreateReqVO {
@Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "流程定义编号不能为空")
private String processDefinitionId;
@Schema(description = "变量实例(动态表单)")
private Map<String, Object> variables;
@Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}")
private Map<String, List<Long>> startUserSelectAssignees;
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
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 = "管理后台 - 流程实例分页 Request VO")
@Data
public class BpmProcessInstancePageReqVO extends PageParam {
@Schema(description = "流程名称", example = "芋道")
private String name;
@Schema(description = "流程定义的标识", example = "2048")
private String processDefinitionKey; // 精准匹配
@Schema(description = "流程实例的状态", example = "1")
@InEnum(BpmProcessInstanceStatusEnum.class)
private Integer status;
@Schema(description = "流程分类", example = "1")
private String category;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] endTime;
@Schema(description = "发起用户编号", example = "1024")
private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数
@Schema(description = "动态表单字段查询 JSON Str", example = "{}")
private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换
}

View File

@@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 流程实例的 Response VO")
@Data
public class BpmProcessInstanceRespVO {
@Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "流程摘要")
private List<KeyValue<String, String>> summary; // 只有流程表单,才有摘要!
@Schema(description = "流程分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private String category;
@Schema(description = "流程分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假")
private String categoryName;
@Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举
@Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime startTime;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "持续时间", example = "1000")
private Long durationInMillis;
@Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, Object> formVariables;
@Schema(description = "业务的唯一标识-例如说,请假申请的编号", example = "1")
private String businessKey;
/**
* 发起流程的用户
*/
private UserSimpleBaseVO startUser;
@Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private String processDefinitionId;
/**
* 流程定义
*/
private BpmProcessDefinitionRespVO processDefinition;
/**
* 当前审批中的任务
*/
private List<Task> tasks; // 仅在流程实例分页才返回
@Schema(description = "流程任务")
@Data
public static class Task {
@Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
@JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser
private Long assignee;
@Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
private UserSimpleBaseVO assigneeUser;
}
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 通过流程任务的 Request VO")
@Data
public class BpmTaskApproveReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "审批意见", example = "不错不错!")
private String reason;
@Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
private String signPicUrl;
@Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, Object> variables;
@Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}")
private Map<String, List<Long>> nextAssignees; // 为什么是 Map而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import java.util.Collection;
@Schema(description = "管理后台 - 抄送流程任务的 Request VO")
@Data
public class BpmTaskCopyReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
@NotEmpty(message = "抄送用户不能为空")
private Collection<Long> copyUserIds;
@Schema(description = "抄送意见", example = "帮忙看看!")
private String reason;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 委派流程任务的 Request VO")
@Data
public class BpmTaskDelegateReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "被委派人 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "被委派人 ID 不能为空")
private Long delegateUserId;
@Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅")
@NotEmpty(message = "委派原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 流程任务的的分页 Request VO") // 待办、已办,都使用该分页
@Data
public class BpmTaskPageReqVO extends PageParam {
@Schema(description = "流程任务名", example = "芋道")
private String name;
@Schema(description = "流程分类", example = "1")
private String category;
@Schema(description = "流程定义的标识", example = "2048")
private String processDefinitionKey; // 精准匹配
@Schema(description = "创建时间")
@DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 不通过流程任务的 Request VO")
@Data
public class BpmTaskRejectReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
private String reason;
}

View File

@@ -0,0 +1,128 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 流程任务 Response VO")
@Data
public class BpmTaskRespVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime endTime;
@Schema(description = "持续时间", example = "1000")
private Long durationInMillis;
@Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer status; // 参见 BpmTaskStatusEnum 枚举
@Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private String reason;
@Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
@JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser
private Long owner;
/**
* 负责人的用户信息
*/
private UserSimpleBaseVO ownerUser;
@Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048")
@JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser
private Long assignee;
/**
* 审核的用户信息
*/
private UserSimpleBaseVO assigneeUser;
@Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one")
private String taskDefinitionKey;
@Schema(description = "所属流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8888")
private String processInstanceId;
/**
* 所属流程实例
*/
private ProcessInstance processInstance;
@Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String parentTaskId;
@Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask")
private List<BpmTaskRespVO> children; // 由加签生成,包含多层子任务
@Schema(description = "表单编号", example = "1024")
private Long formId;
@Schema(description = "表单名字", example = "请假表单")
private String formName;
@Schema(description = "表单的配置JSON 字符串")
private String formConf;
@Schema(description = "表单项的数组")
private List<String> formFields;
@Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<String, Object> formVariables;
@Schema(description = "操作按钮设置值")
private Map<Integer, OperationButtonSetting> buttonsSetting;
@Schema(description = "是否需要签名", example = "false")
private Boolean signEnable;
@Schema(description = "是否填写审批意见", example = "false")
private Boolean reasonRequire;
@Schema(description = "节点类型", example = "10")
private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。
@Data
@Schema(description = "流程实例")
public static class ProcessInstance {
@Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private String id;
@Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
private String name;
@Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
@Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private String processDefinitionId;
@Schema(description = "流程摘要", example = "[]")
private List<KeyValue<String, String>> summary; // 只有流程表单,才有摘要!
/**
* 发起人的用户信息
*/
private UserSimpleBaseVO startUser;
}
@Data
@Schema(description = "操作按钮设置")
public static class OperationButtonSetting {
@Schema(description = "显示名称", example = "审批")
private String displayName;
@Schema(description = "是否启用", example = "true")
private Boolean enable;
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 退回流程任务的 Request VO")
@Data
public class BpmTaskReturnReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "退回到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotEmpty(message = "退回到的任务 Key 不能为空")
private String targetTaskDefinitionKey;
@Schema(description = "退回意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
@NotEmpty(message = "退回意见不能为空")
private String reason;
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.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

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@Schema(description = "管理后台 - 加签任务的删除(减签) Request VO")
@Data
public class BpmTaskSignDeleteReqVO {
@Schema(description = "被减签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要减签")
@NotEmpty(message = "加签原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 流程任务的转办 Request VO")
@Data
public class BpmTaskTransferReqVO {
@Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@Schema(description = "新审批人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
@NotNull(message = "新审批人的用户编号不能为空")
private Long assigneeUserId;
@Schema(description = "转办原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅")
@NotEmpty(message = "转办原因不能为空")
private String reason;
}

View File

@@ -0,0 +1,4 @@
/**
* 占位
*/
package cn.iocoder.yudao.module.bpm.controller.app;

View File

@@ -0,0 +1,6 @@
/**
* 提供 RESTful API 给前端:
* 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目
* 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
*/
package cn.iocoder.yudao.module.bpm.controller;

View File

@@ -0,0 +1,132 @@
package cn.iocoder.yudao.module.bpm.convert.definition;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/**
* 流程模型 Convert
*
* @author yunlongn
*/
@Mapper
public interface BpmModelConvert {
BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class);
default List<BpmModelRespVO> buildModelList(List<Model> list,
Map<Long, BpmFormDO> formMap,
Map<String, BpmCategoryDO> categoryMap,
Map<String, Deployment> deploymentMap,
Map<String, ProcessDefinition> processDefinitionMap,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
List<BpmModelRespVO> result = convertList(list, model -> {
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null;
BpmCategoryDO category = categoryMap.get(model.getCategory());
Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null;
ProcessDefinition processDefinition = model.getDeploymentId() != null ?
processDefinitionMap.get(model.getDeploymentId()) : null;
List<AdminUserRespDTO> startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null;
List<DeptRespDTO> startDepts = metaInfo != null ? convertList(metaInfo.getStartDeptIds(), deptMap::get) : null;
return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers, startDepts);
});
// 排序
result.sort(Comparator.comparing(BpmModelMetaInfoVO::getSort));
return result;
}
default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes, BpmSimpleModelNodeVO simpleModel) {
BpmModelMetaInfoVO metaInfo = parseMetaInfo(model);
BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null, null);
if (ArrayUtil.isNotEmpty(bpmnBytes)) {
modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes));
}
modelVO.setSimpleModel(simpleModel);
return modelVO;
}
default BpmModelRespVO buildModel0(Model model,
BpmModelMetaInfoVO metaInfo, BpmFormDO form, BpmCategoryDO category,
Deployment deployment, ProcessDefinition processDefinition,
List<AdminUserRespDTO> startUsers, List<DeptRespDTO> startDepts) {
BpmModelRespVO modelRespVO = new BpmModelRespVO().setId(model.getId()).setName(model.getName())
.setKey(model.getKey()).setCategory(model.getCategory())
.setCreateTime(DateUtils.of(model.getCreateTime()));
// Form
BeanUtils.copyProperties(metaInfo, modelRespVO);
if (form != null) {
modelRespVO.setFormName(form.getName());
}
// Category
if (category != null) {
modelRespVO.setCategoryName(category.getName());
}
// ProcessDefinition
if (processDefinition != null) {
modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ?
SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
if (deployment != null) {
modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime()));
}
}
// User、Dept
modelRespVO.setStartUsers(BeanUtils.toBean(startUsers, UserSimpleBaseVO.class))
.setStartDepts(BeanUtils.toBean(startDepts, DeptSimpleBaseVO.class));
return modelRespVO;
}
default void copyToModel(Model model, BpmModelSaveReqVO reqVO) {
model.setName(reqVO.getName());
model.setKey(reqVO.getKey());
model.setCategory(reqVO.getCategory());
model.setMetaInfo(JsonUtils.toJsonString(BeanUtils.toBean(reqVO, BpmModelMetaInfoVO.class)));
}
default BpmModelMetaInfoVO parseMetaInfo(Model model) {
BpmModelMetaInfoVO vo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoVO.class);
if (vo == null) {
return null;
}
if (vo.getManagerUserIds() == null) {
vo.setManagerUserIds(Collections.emptyList());
}
if (vo.getStartUserIds() == null) {
vo.setStartUserIds(Collections.emptyList());
}
// 如果为空,兜底处理,使用 createTime 创建时间
if (vo.getSort() == null) {
vo.setSort(model.getCreateTime().getTime());
}
return vo;
}
}

View File

@@ -0,0 +1,99 @@
package cn.iocoder.yudao.module.bpm.convert.definition;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
/**
* Bpm 流程定义的 Convert
*
* @author yunlong.li
*/
@Mapper
public interface BpmProcessDefinitionConvert {
BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class);
default PageResult<BpmProcessDefinitionRespVO> buildProcessDefinitionPage(PageResult<ProcessDefinition> page,
Map<String, Deployment> deploymentMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,
Map<Long, BpmFormDO> formMap,
Map<String, BpmCategoryDO> categoryMap) {
List<BpmProcessDefinitionRespVO> list = buildProcessDefinitionList(page.getList(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap);
return new PageResult<>(list, page.getTotal());
}
default List<BpmProcessDefinitionRespVO> buildProcessDefinitionList(List<ProcessDefinition> list,
Map<String, Deployment> deploymentMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap,
Map<Long, BpmFormDO> formMap,
Map<String, BpmCategoryDO> categoryMap) {
List<BpmProcessDefinitionRespVO> result = CollectionUtils.convertList(list, definition -> {
Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class);
BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class);
BpmFormDO form = null;
if (processDefinitionInfo != null) {
form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class);
}
BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class);
return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null);
});
// 排序
result.sort(Comparator.comparing(BpmProcessDefinitionRespVO::getSort));
return result;
}
default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition,
Deployment deployment,
BpmProcessDefinitionInfoDO processDefinitionInfo,
BpmFormDO form,
BpmCategoryDO category,
BpmnModel bpmnModel) {
BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class);
respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
// Deployment
if (deployment != null) {
respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime()));
}
// BpmProcessDefinitionInfoDO
if (processDefinitionInfo != null) {
copyTo(processDefinitionInfo, respVO);
// Form
if (form != null) {
respVO.setFormName(form.getName());
}
}
// Category
if (category != null) {
respVO.setCategoryName(category.getName());
}
// BpmnModel
if (bpmnModel != null) {
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
}
return respVO;
}
@Mapping(source = "from.id", target = "to.id", ignore = true)
void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.bpm.convert.message;
import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.Map;
@Mapper
public interface BpmMessageConvert {
BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class);
@Mapping(target = "mobile", ignore = true)
@Mapping(source = "userId", target = "userId")
@Mapping(source = "templateCode", target = "templateCode")
@Mapping(source = "templateParams", target = "templateParams")
SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map<String, Object> templateParams);
}

View File

@@ -0,0 +1,6 @@
/**
* 提供 POJO 类的实体转换
*
* 目前使用 MapStruct 框架
*/
package cn.iocoder.yudao.module.bpm.convert;

View File

@@ -0,0 +1,295 @@
package cn.iocoder.yudao.module.bpm.convert.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* 流程实例 Convert
*
* @author 芋道源码
*/
@Mapper
public interface BpmProcessInstanceConvert {
BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);
default PageResult<BpmProcessInstanceRespVO> buildProcessInstancePage(PageResult<HistoricProcessInstance> pageResult,
Map<String, ProcessDefinition> processDefinitionMap,
Map<String, BpmCategoryDO> categoryMap,
Map<String, List<Task>> taskMap,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {
PageResult<BpmProcessInstanceRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class);
for (int i = 0; i < pageResult.getList().size(); i++) {
BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i);
respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i)));
MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(),
processDefinition -> respVO.setCategory(processDefinition.getCategory())
.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)));
MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName()));
respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class));
// user
if (userMap != null) {
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId()));
if (startUser != null) {
respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName()));
}
if (CollUtil.isNotEmpty(respVO.getTasks())) {
respVO.getTasks().forEach(task -> {
AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee());
if (assigneeUser!= null) {
task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class));
MapUtils.findAndThen(deptMap, assigneeUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName()));
}
});
}
}
// 摘要
respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()),
pageResult.getList().get(i).getProcessVariables()));
// 表单
respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables());
}
return vpPageResult;
}
default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance,
ProcessDefinition processDefinition,
BpmProcessDefinitionInfoDO processDefinitionInfo,
AdminUserRespDTO startUser,
DeptRespDTO dept) {
BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance))
.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
// definition
respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));
copyTo(processDefinitionInfo, respVO.getProcessDefinition());
// user
if (startUser != null) {
respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
if (dept != null) {
respVO.getStartUser().setDeptName(dept.getName());
}
}
return respVO;
}
@Mapping(source = "from.id", target = "to.id", ignore = true)
void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to);
default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {
return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
.setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
}
default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance) {
return new BpmMessageSendWhenProcessInstanceApproveReqDTO()
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()))
.setProcessInstanceId(instance.getId())
.setProcessInstanceName(instance.getName());
}
default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) {
return new BpmMessageSendWhenProcessInstanceRejectReqDTO()
.setProcessInstanceName(instance.getName())
.setProcessInstanceId(instance.getId())
.setReason(reason)
.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
}
default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance,
List<HistoricTaskInstance> taskInstances,
BpmnModel bpmnModel,
BpmSimpleModelNodeVO simpleModel,
Set<String> unfinishedTaskActivityIds,
Set<String> finishedTaskActivityIds,
Set<String> finishedSequenceFlowActivityIds,
Set<String> rejectTaskActivityIds,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO();
// 基本信息
respVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class, o -> o
.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)))
.setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap)));
respVO.setTasks(convertList(taskInstances, task -> BeanUtils.toBean(task, BpmTaskRespVO.class)
.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap))
.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap))));
respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));
respVO.setSimpleModel(simpleModel);
// 进度信息
respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds)
.setFinishedTaskActivityIds(finishedTaskActivityIds)
.setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds)
.setRejectedTaskActivityIds(rejectTaskActivityIds);
return respVO;
}
default UserSimpleBaseVO buildUser(String userIdStr,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
if (StrUtil.isEmpty(userIdStr)) {
return null;
}
Long userId = NumberUtils.parseLong(userIdStr);
return buildUser(userId, userMap, deptMap);
}
default UserSimpleBaseVO buildUser(Long userId,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
if (userId == null) {
return null;
}
AdminUserRespDTO user = userMap.get(userId);
if (user == null) {
return null;
}
UserSimpleBaseVO userVO = BeanUtils.toBean(user, UserSimpleBaseVO.class);
DeptRespDTO dept = user.getDeptId() != null ? deptMap.get(user.getDeptId()) : null;
if (dept != null) {
userVO.setDeptName(dept.getName());
}
return userVO;
}
default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) {
if (task == null) {
return null;
}
return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
.setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));
}
default Set<Long> parseUserIds(HistoricProcessInstance processInstance,
List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,
BpmTaskRespVO todoTask) {
Set<Long> userIds = new HashSet<>();
if (processInstance != null) {
userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
}
for (BpmApprovalDetailRespVO.ActivityNode activityNode : activityNodes) {
CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getAssignee));
CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getOwner));
CollUtil.addAll(userIds, activityNode.getCandidateUserIds());
}
if (todoTask != null) {
CollUtil.addIfAbsent(userIds, todoTask.getAssignee());
CollUtil.addIfAbsent(userIds, todoTask.getOwner());
if (CollUtil.isNotEmpty(todoTask.getChildren())) {
CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getAssignee));
CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getOwner));
}
}
return userIds;
}
default Set<Long> parseUserIds02(HistoricProcessInstance processInstance,
List<HistoricTaskInstance> tasks) {
Set<Long> userIds = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
tasks.forEach(task -> {
CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getAssignee())));
CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getOwner())));
});
return userIds;
}
default BpmApprovalDetailRespVO buildApprovalDetail(BpmnModel bpmnModel,
ProcessDefinition processDefinition,
BpmProcessDefinitionInfoDO processDefinitionInfo,
HistoricProcessInstance processInstance,
Integer processInstanceStatus,
List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,
BpmTaskRespVO todoTask,
Map<String, String> formFieldsPermission,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
// 1.1 流程实例
BpmProcessInstanceRespVO processInstanceResp = null;
if (processInstance != null) {
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
DeptRespDTO dept = startUser != null ? deptMap.get(startUser.getDeptId()) : null;
processInstanceResp = buildProcessInstance(processInstance, null, null, startUser, dept);
}
// 1.2 流程定义
BpmProcessDefinitionRespVO definitionResp = BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
processDefinition, null, processDefinitionInfo, null, null, bpmnModel);
// 1.3 流程节点
activityNodes.forEach(approveNode -> {
if (approveNode.getTasks() != null) {
approveNode.getTasks().forEach(task -> {
task.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap));
task.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap));
});
}
approveNode.setCandidateUsers(convertList(approveNode.getCandidateUserIds(), userId -> buildUser(userId, userMap, deptMap)));
});
// 1.4 待办任务
if (todoTask != null) {
todoTask.setAssigneeUser(buildUser(todoTask.getAssignee(), userMap, deptMap));
todoTask.setOwnerUser(buildUser(todoTask.getOwner(), userMap, deptMap));
if (CollUtil.isNotEmpty(todoTask.getChildren())) {
todoTask.getChildren().forEach(childTask -> {
childTask.setAssigneeUser(buildUser(childTask.getAssignee(), userMap, deptMap));
childTask.setOwnerUser(buildUser(childTask.getOwner(), userMap, deptMap));
});
}
}
// 2. 拼接起来
return new BpmApprovalDetailRespVO().setStatus(processInstanceStatus)
.setProcessDefinition(definitionResp)
.setProcessInstance(processInstanceResp)
.setFormFieldsPermission(formFieldsPermission)
.setTodoTask(todoTask)
.setActivityNodes(activityNodes);
}
}

View File

@@ -0,0 +1,221 @@
package cn.iocoder.yudao.module.bpm.convert.task;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Date;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
/**
* Bpm 任务 Convert
*
* @author 芋道源码
*/
@Mapper
public interface BpmTaskConvert {
BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
default PageResult<BpmTaskRespVO> buildTodoTaskPage(PageResult<Task> pageResult,
Map<String, ProcessInstance> processInstanceMap,
Map<Long, AdminUserRespDTO> userMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {
return BeanUtils.toBean(pageResult, BpmTaskRespVO.class, taskVO -> {
ProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId());
if (processInstance == null) {
return;
}
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
taskVO.getProcessInstance().setCreateTime(DateUtils.of(processInstance.getStartTime()));
// 摘要
taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
processInstance.getProcessVariables()));
});
}
default PageResult<BpmTaskRespVO> buildTaskPage(PageResult<HistoricTaskInstance> pageResult,
Map<String, HistoricProcessInstance> processInstanceMap,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap,
Map<String, BpmProcessDefinitionInfoDO> processDefinitionInfoMap) {
List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> {
BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
// 用户信息
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
if (assignUser != null) {
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));
findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName()));
}
// 流程实例
HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId());
if (processInstance != null) {
AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class));
taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
// 摘要
taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()),
processInstance.getProcessVariables()));
}
return taskVO;
});
return new PageResult<>(taskVOList, pageResult.getTotal());
}
default List<BpmTaskRespVO> buildTaskListByProcessInstanceId(List<HistoricTaskInstance> taskList,
Map<Long, BpmFormDO> formMap,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
return CollectionUtils.convertList(taskList, task -> {
// 特殊:已取消的任务,不返回
BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
Integer taskStatus = FlowableUtils.getTaskStatus(task);
if (BpmTaskStatusEnum.isCancelStatus(taskStatus)) {
return null;
}
taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task));
// 表单信息
BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class);
if (form != null) {
taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf())
.setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));
}
// 用户信息
buildTaskAssignee(taskVO, task.getAssignee(), userMap, deptMap);
buildTaskOwner(taskVO, task.getOwner(), userMap, deptMap);
return taskVO;
});
}
default List<BpmTaskRespVO> buildTaskListByParentTaskId(List<Task> taskList,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> {
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee()));
if (assignUser != null) {
taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));
DeptRespDTO dept = deptMap.get(assignUser.getDeptId());
if (dept != null) {
taskVO.getAssigneeUser().setDeptName(dept.getName());
}
}
AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner()));
if (ownerUser != null) {
taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class));
findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName()));
}
}));
}
default BpmTaskRespVO buildTodoTask(Task todoTask, List<Task> childrenTasks,
Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonsSetting,
BpmFormDO form) {
BpmTaskRespVO bpmTaskRespVO = BeanUtils.toBean(todoTask, BpmTaskRespVO.class)
.setStatus(FlowableUtils.getTaskStatus(todoTask)).setReason(FlowableUtils.getTaskReason(todoTask))
.setButtonsSetting(buttonsSetting)
.setChildren(convertList(childrenTasks, childTask -> BeanUtils.toBean(childTask, BpmTaskRespVO.class)
.setStatus(FlowableUtils.getTaskStatus(childTask))));
if (form != null) {
bpmTaskRespVO.setFormId(form.getId()).setFormName(form.getName())
.setFormConf(form.getConf()).setFormFields(form.getFields());
}
return bpmTaskRespVO;
}
default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser,
Task task) {
BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO();
reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId())
.setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId())
.setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName())
.setAssigneeUserId(NumberUtils.parseLong(task.getAssignee()));
return reqDTO;
}
default void buildTaskOwner(BpmTaskRespVO task, String taskOwner,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(taskOwner));
if (ownerUser != null) {
task.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class));
findAndThen(deptMap, ownerUser.getDeptId(), dept -> task.getOwnerUser().setDeptName(dept.getName()));
}
}
default void buildTaskChildren(BpmTaskRespVO task, Map<String, List<Task>> childrenTaskMap,
Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {
List<Task> childTasks = childrenTaskMap.get(task.getId());
if (CollUtil.isNotEmpty(childTasks)) {
task.setChildren(
convertList(childTasks, childTask -> {
BpmTaskRespVO childTaskVO = BeanUtils.toBean(childTask, BpmTaskRespVO.class);
childTaskVO.setStatus(FlowableUtils.getTaskStatus(childTask));
buildTaskOwner(childTaskVO, childTask.getOwner(), userMap, deptMap);
buildTaskAssignee(childTaskVO, childTask.getAssignee(), userMap, deptMap);
return childTaskVO;
})
);
}
}
default void buildTaskAssignee(BpmTaskRespVO task, String taskAssignee,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(taskAssignee));
if (assignUser != null) {
task.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class));
findAndThen(deptMap, assignUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName()));
}
}
/**
* 将父任务的属性,拷贝到子任务(加签任务)
* <p>
* 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。
* 使用 mapstruct 会将里面嵌套的各个属性值都设置进去,会出现意想不到的问题。
*
* @param parentTask 父任务
* @param childTask 加签任务
*/
default void copyTo(TaskEntityImpl parentTask, TaskEntityImpl childTask) {
childTask.setName(parentTask.getName());
childTask.setDescription(parentTask.getDescription());
childTask.setCategory(parentTask.getCategory());
childTask.setParentTaskId(parentTask.getId());
childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId());
childTask.setProcessInstanceId(parentTask.getProcessInstanceId());
childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey());
childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId());
childTask.setPriority(parentTask.getPriority());
childTask.setCreateTime(new Date());
childTask.setTenantId(parentTask.getTenantId());
}
}

View File

@@ -0,0 +1 @@
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>

View File

@@ -0,0 +1,53 @@
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.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* BPM 流程分类 DO
*
* @author 芋道源码
*/
@TableName("bpm_category")
@KeySequence("bpm_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 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

@@ -0,0 +1,60 @@
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.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* BPM 工作流的表单定义
* 用于工作流的申请表单,需要动态配置的场景
*
* @author 芋道源码
*/
@TableName(value = "bpm_form", autoResultMap = true)
@KeySequence("bpm_form_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmFormDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 表单名
*/
private String name;
/**
* 状态
*/
private Integer status;
/**
* 表单的配置
*/
private String conf;
/**
* 表单项的数组
*
* 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串,直接保存
* 定义https://github.com/JakHuang/form-generator/issues/46
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> fields;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,222 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import java.util.List;
/**
* BPM 流程定义的拓信息
* 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表
*
* @author 芋道源码
*/
@TableName(value = "bpm_process_definition_info", autoResultMap = true)
@KeySequence("bpm_process_definition_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmProcessDefinitionInfoDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 流程定义的编号
*
* 关联 {@link ProcessDefinition#getId()} 属性
*/
private String processDefinitionId;
/**
* 流程模型的编号
*
* 关联 {@link Model#getId()} 属性
*/
private String modelId;
/**
* 流程模型的类型
*
* 枚举 {@link BpmModelTypeEnum}
*/
private Integer modelType;
/**
* 流程分类的编码
*
* 关联 {@link BpmCategoryDO#getCode()}
*
* 为什么要存储?原因是,{@link ProcessDefinition#getCategory()} 无法设置
*/
private String category;
/**
* 图标
*/
private String icon;
/**
* 描述
*/
private String description;
/**
* 表单类型
*
* 枚举 {@link BpmModelFormTypeEnum}
*/
private Integer formType;
/**
* 动态表单编号
*
* 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
*
* 关联 {@link BpmFormDO#getId()}
*/
private Long formId;
/**
* 表单的配置
*
* 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
*
* 冗余 {@link BpmFormDO#getConf()}
*/
private String formConf;
/**
* 表单项的数组
*
* 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时
*
* 冗余 {@link BpmFormDO#getFields()}
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> formFields;
/**
* 自定义表单的提交路径,使用 Vue 的路由地址
*
* 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时
*/
private String formCustomCreatePath;
/**
* 自定义表单的查看路径,使用 Vue 的路由地址
*
* 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时
*/
private String formCustomViewPath;
/**
* SIMPLE 设计器模型数据 json 格式
*
* 目的:当使用仿钉钉设计器时。流程模型发布的时候,需要保存流程模型设计器的快照数据。
*/
private String simpleModel;
/**
* 是否可见
*
* 目的:如果 false 不可见,则不展示在“发起流程”的列表里
*/
private Boolean visible;
/**
* 排序值
*/
private Long sort;
/**
* 可发起用户编号数组
*
* 关联 {@link AdminUserRespDTO#getId()} 字段的数组
*
* 如果为空,则表示“全部可以发起”!
*
* 它和 {@link #visible} 的区别在于:
* 1. {@link #visible} 只是决定是否可见。即使不可见,还是可以发起
* 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起,则他也是不可见的
*/
@TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
private List<Long> startUserIds;
/**
* 可发起部门编号数组
*
* 关联 {@link AdminUserRespDTO#getDeptId()} 字段的数组
*/
@TableField(typeHandler = LongListTypeHandler.class)
private List<Long> startDeptIds;
/**
* 可管理用户编号数组
*
* 关联 {@link AdminUserRespDTO#getId()} 字段的数组
*/
@TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤
private List<Long> managerUserIds;
/**
* 是否允许撤销审批中的申请
*/
private Boolean allowCancelRunningProcess;
/**
* 流程 ID 规则
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.ProcessIdRule processIdRule;
/**
* 自动去重类型
*
* 枚举 {@link BpmAutoApproveTypeEnum}
*/
private Integer autoApprovalType;
/**
* 标题设置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.TitleSetting titleSetting;
/**
* 摘要设置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.SummarySetting summarySetting;
/**
* 流程前置通知设置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.HttpRequestSetting processBeforeTriggerSetting;
/**
* 流程后置通知设置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting;
/**
* 任务前置通知设置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.HttpRequestSetting taskBeforeTriggerSetting;
/**
* 任务后置通知设置
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting;
}

View File

@@ -0,0 +1,44 @@
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_process_expression")
@KeySequence("bpm_process_expression_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 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

@@ -0,0 +1,73 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmProcessListenerTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
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")
@KeySequence("bpm_process_listener_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@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 BpmProcessListenerTypeEnum}
*
* 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 时start、end
* 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

@@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.definition;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Set;
/**
* BPM 用户组
*
* @author 芋道源码
*/
@TableName(value = "bpm_user_group", autoResultMap = true)
@KeySequence("bpm_user_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmUserGroupDO extends BaseDO {
/**
* 编号,自增
*/
@TableId
private Long id;
/**
* 组名
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 成员用户编号数组
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Set<Long> userIds;
}

View File

@@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.oa;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* OA 请假申请 DO
*
* {@link #day} 请假天数,目前先简单做。一般是分成请假上午和下午,可以是 1 整天,可以是 0.5 半天
*
* @author jason
* @author 芋道源码
*/
@TableName("bpm_oa_leave")
@KeySequence("bpm_oa_leave_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmOALeaveDO extends BaseDO {
/**
* 请假表单主键
*/
@TableId
private Long id;
/**
* 申请人的用户编号
*
* 关联 AdminUserDO 的 id 属性
*/
private Long userId;
/**
* 请假类型
*/
private String type;
/**
* 原因
*/
private String reason;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* 请假天数
*/
private Long day;
/**
* 审批结果
*
* 枚举 {@link BpmTaskStatusEnum}
* 考虑到简单,所以直接复用了 BpmProcessInstanceStatusEnum 枚举,也可以自己定义一个枚举哈
*/
private Integer status;
/**
* 对应的流程编号
*
* 关联 ProcessInstance 的 id 属性
*/
private String processInstanceId;
}

View File

@@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.bpm.dal.dataobject.task;
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 org.flowable.bpmn.model.FlowNode;
import org.flowable.task.api.history.HistoricTaskInstance;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 流程抄送 DO
*
* @author kyle
* @since 2024-01-22
*/
@TableName(value = "bpm_process_instance_copy", autoResultMap = true)
@KeySequence("bpm_process_instance_copy_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmProcessInstanceCopyDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 发起人 Id
*
* 冗余 ProcessInstance 的 startUserId 字段
*/
private Long startUserId;
/**
* 流程名
*
* 冗余 ProcessInstance 的 name 字段
*/
private String processInstanceName;
/**
* 流程实例的编号
*
* 关联 ProcessInstance 的 id 属性
*/
private String processInstanceId;
/**
* 流程实例的流程定义编号
*
* 关联 ProcessInstance 的 processDefinitionId 属性
*/
private String processDefinitionId;
/**
* 流程分类
*
* 冗余 ProcessInstance 的 category 字段
*/
private String category;
/**
* 流程活动的编号
* <p/>
*
* 冗余 {@link FlowNode#getId()},对应 BPMN XML 节点编号
* 原因:用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask),没有 taskId只有 activityId
*/
private String activityId;
/**
* 流程活动的名字
*
* 冗余 {@link FlowNode#getName()}
*/
private String activityName;
/**
* 流程活动的编号
*
* 关联 {@link HistoricTaskInstance#getId()}
*/
private String taskId;
/**
* 用户编号(被抄送的用户编号)
*
* 关联 system_users 的 id 属性
*/
private Long userId;
/**
* 抄送意见
*/
private String reason;
}

View File

@@ -0,0 +1,46 @@
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

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
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.QueryWrapperX;
import org.apache.ibatis.annotations.Mapper;
/**
* 动态表单 Mapper
*
* @author 风里雾里
*/
@Mapper
public interface BpmFormMapper extends BaseMapperX<BpmFormDO> {
default PageResult<BpmFormDO> selectPage(BpmFormPageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<BpmFormDO>()
.likeIfPresent("name", reqVO.getName())
.orderByDesc("id"));
}
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
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);
}
default void updateByModelId(String modelId, BpmProcessDefinitionInfoDO updateObj) {
update(updateObj,
new LambdaQueryWrapperX<BpmProcessDefinitionInfoDO>().eq(BpmProcessDefinitionInfoDO::getModelId, modelId));
}
}

View File

@@ -0,0 +1,26 @@
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

@@ -0,0 +1,27 @@
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

@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.definition;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO;
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 org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 用户组 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface BpmUserGroupMapper extends BaseMapperX<BpmUserGroupDO> {
default PageResult<BpmUserGroupDO> selectPage(BpmUserGroupPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmUserGroupDO>()
.likeIfPresent(BpmUserGroupDO::getName, reqVO.getName())
.eqIfPresent(BpmUserGroupDO::getStatus, reqVO.getStatus())
.betweenIfPresent(BpmUserGroupDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(BpmUserGroupDO::getId));
}
default List<BpmUserGroupDO> selectListByStatus(Integer status) {
return selectList(BpmUserGroupDO::getStatus, status);
}
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;
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 org.apache.ibatis.annotations.Mapper;
/**
* 请假申请 Mapper
*
* @author jason
* @author 芋道源码
*/
@Mapper
public interface BpmOALeaveMapper extends BaseMapperX<BpmOALeaveDO> {
default PageResult<BpmOALeaveDO> selectPage(Long userId, BpmOALeavePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmOALeaveDO>()
.eqIfPresent(BpmOALeaveDO::getUserId, userId)
.eqIfPresent(BpmOALeaveDO::getStatus, reqVO.getStatus())
.eqIfPresent(BpmOALeaveDO::getType, reqVO.getType())
.likeIfPresent(BpmOALeaveDO::getReason, reqVO.getReason())
.betweenIfPresent(BpmOALeaveDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(BpmOALeaveDO::getId));
}
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.bpm.dal.mysql.task;
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.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BpmProcessInstanceCopyMapper extends BaseMapperX<BpmProcessInstanceCopyDO> {
default PageResult<BpmProcessInstanceCopyDO> selectPage(Long loginUserId, BpmProcessInstanceCopyPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<BpmProcessInstanceCopyDO>()
.eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId)
.likeIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceName, reqVO.getProcessInstanceName())
.betweenIfPresent(BpmProcessInstanceCopyDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(BpmProcessInstanceCopyDO::getId));
}
default void deleteByProcessInstanceId(String processInstanceId) {
delete(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId);
}
}

View File

@@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import static cn.hutool.core.date.DatePattern.*;
/**
* BPM 流程 Id 编码的 Redis DAO
*
* @author Lesan
*/
@Repository
public class BpmProcessIdRedisDAO {
@Resource
private StringRedisTemplate stringRedisTemplate;
/**
* 生成序号,使用定义的 processIdRule 规则生成
*
* @param processIdRule 规则
* @return 序号
*/
public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) {
// 生成日期前缀
String infix = "";
switch (processIdRule.getInfix()) {
case "DAY":
infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN);
break;
case "HOUR":
infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HH");
break;
case "MINUTE":
infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HHmm");
break;
case "SECOND":
infix = DateUtil.format(LocalDateTime.now(), PURE_DATETIME_PATTERN);
break;
}
// 生成序号
String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();
String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;
Long no = stringRedisTemplate.opsForValue().increment(key);
if (StrUtil.isEmpty(infix)) {
// 特殊:没有前缀,则不能过期,不能每次都是从 0 开始
stringRedisTemplate.expire(key, Duration.ofDays(1L));
}
return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
}
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.bpm.dal.redis;
/**
* BPM Redis Key 枚举类
*
* @author 芋道源码
*/
public interface RedisKeyConstants {
/**
* 流程 ID 的缓存
*/
String BPM_PROCESS_ID = "bpm:process_id:";
}

Some files were not shown because too many files have changed in this diff Show More