!62 工作流的流程任务相关

Merge pull request !62 from 芋道源码/feature/activiti
This commit is contained in:
芋道源码 2022-01-09 14:48:39 +00:00 committed by Gitee
commit 8a3488f3d1
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
83 changed files with 3224 additions and 2139 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,4 @@
### 请求 /bpm/process-definition/list 接口 => 成功
GET {{baseUrl}}/bpm/process-definition/list?suspensionState=1
tenant-id: 1
Authorization: Bearer {{token}}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionListReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionPageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionPageReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@ -17,6 +19,8 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "流程定义")
@ -36,14 +40,11 @@ public class BpmProcessDefinitionController {
return success(bpmDefinitionService.getProcessDefinitionPage(pageReqVO));
}
// TODO 芋艿需要重写该方法
@GetMapping(value = "/getStartForm")
public CommonResult<String> getStartForm(@RequestParam("processKey") String processKey){
// final ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().
// processDefinitionKey(processKey).latestVersion().singleResult();
// processRuntime.processDefinition(processDefinition.getId()).getFormKey();
// TODO 这样查似乎有问题 暂时写死
return success("/flow/leave/apply");
@GetMapping ("/list")
@ApiOperation(value = "获得流程定义列表")
public CommonResult<List<BpmProcessDefinitionRespVO>> getProcessDefinitionList(
BpmProcessDefinitionListReqVO listReqVO) {
return success(bpmDefinitionService.getProcessDefinitionList(listReqVO));
}
@GetMapping ("/get-bpmn-xml")

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@ApiModel("流程定义列表 Request VO")
public class BpmProcessDefinitionListReqVO extends PageParam {
@ApiModelProperty(value = "中断状态", example = "1", notes = "参见 SuspensionState 枚举")
private Integer suspensionState;
}

View File

@ -20,5 +20,4 @@ public class BpmProcessDefinitionPageItemRespVO extends BpmProcessDefinitionResp
@ApiModelProperty(value = "部署时间", required = true)
private Date deploymentTime;
}

View File

@ -1,13 +1,29 @@
### 请求 /login 接口 => 成功
### 请求 /bpm/process-instance/create 接口 => 成功
POST {{baseUrl}}/bpm/process-instance/create
Content-Type: application/json
tenant-id: 1
Authorization: Bearer {{token}}
{
"processDefinitionId": "leave:7:20ada39c-6c95-11ec-88b1-cacd34981f8e",
"processDefinitionId": "gateway_test:2:00e52d8e-701b-11ec-aca9-a2380e71991a",
"variables": {
"a": 1,
"b": "2"
}
}
### 请求 /bpm/process-instance/cancel 接口 => 成功
DELETE {{baseUrl}}/bpm/process-instance/cancel
Content-Type: application/json
tenant-id: 1
Authorization: Bearer {{token}}
{
"id": "b9220387-7088-11ec-bcae-a2380e71991a",
"reason": "我就取消"
}
### 请求 /bpm/process-instance/my-page 接口 => 成功
GET {{baseUrl}}/bpm/process-instance/my-page
tenant-id: 1
Authorization: Bearer {{token}}

View File

@ -1,15 +1,16 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCancelReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceMyPageReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
@ -26,10 +27,26 @@ public class BpmProcessInstanceController {
@Resource
private BpmProcessInstanceService processInstanceService;
// TODO 芋艿权限
@PostMapping("/create")
@ApiOperation("新建流程实例")
public CommonResult<String> createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) {
return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO));
}
@DeleteMapping("/cancel")
@ApiOperation(value = "取消流程实例", notes = "撤回发起的流程")
public CommonResult<Boolean> cancelProcessInstance(@Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) {
processInstanceService.cancelProcessInstance(getLoginUserId(), cancelReqVO);
return success(true);
}
@GetMapping("/my-page")
@ApiOperation(value = "获得我的实例分页列表", notes = "在【我的流程】菜单中,进行调用")
public CommonResult<PageResult<BpmProcessInstancePageItemRespVO>> getMyProcessInstancePage(
@Valid BpmProcessInstanceMyPageReqVO pageReqVO) {
return success(processInstanceService.getMyProcessInstancePage(getLoginUserId(), pageReqVO));
}
}

View File

@ -0,0 +1,9 @@
### 请求 /bpm/task/todo-page 接口 => 成功
GET {{baseUrl}}/bpm/task/todo-page
tenant-id: 1
Authorization: Bearer {{token}}
### 请求 /bpm/task/done-page 接口 => 成功
GET {{baseUrl}}/bpm/task/done-page?pageSize=100
tenant-id: 1
Authorization: Bearer {{token}}

View File

@ -0,0 +1,79 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
@Api(tags = "流程任务")
@RestController
@RequestMapping("/bpm/task")
@Validated
public class BpmTaskController {
@Resource
private BpmTaskService taskService;
// TODO 芋艿权限validation
@GetMapping("todo-page")
@ApiOperation("获取 Todo 待办任务分页")
public CommonResult<PageResult<BpmTaskTodoPageItemRespVO>> getTodoTaskPage(@Valid BpmTaskTodoPageReqVO pageVO) {
return success(taskService.getTodoTaskPage(getLoginUserId(), pageVO));
}
@GetMapping("done-page")
@ApiOperation("获取 Done 已办任务分页")
public CommonResult<PageResult<BpmTaskDonePageItemRespVO>> getTodoTaskPage(@Valid BpmTaskDonePageReqVO pageVO) {
return success(taskService.getDoneTaskPage(getLoginUserId(), pageVO));
}
@PutMapping("/approve")
@ApiOperation("通过任务")
public CommonResult<Boolean> approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) {
taskService.approveTask(reqVO);
return success(true);
}
@PutMapping("/reject")
@ApiOperation("不通过任务")
public CommonResult<Boolean> rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) {
taskService.rejectTask(reqVO);
return success(true);
}
@PostMapping("/task-steps")
public CommonResult<TaskHandleVO> getTaskSteps(@RequestBody TaskQueryReqVO taskQuery) {
return success(taskService.getTaskSteps(taskQuery));
}
@GetMapping("/process/history-steps")
public CommonResult<List<TaskStepVO>> getHistorySteps(@RequestParam("id") String processInstanceId) {
return success(taskService.getHistorySteps(processInstanceId));
}
/**
* 返回高亮的流转图SVG
* @param processInstanceId 流程Id
*/
@GetMapping("/process/highlight-img")
public void getHighlightImg(@RequestParam String processInstanceId, HttpServletResponse response) throws IOException {
FileResp fileResp = taskService.getHighlightImg(processInstanceId);
ServletUtils.writeAttachment(response, fileResp.getFileName(), fileResp.getFileByte());
}
}

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Map;
@ApiModel("流程实例的取消 Request VO")
@Data
public class BpmProcessInstanceCancelReqVO {
@ApiModelProperty(value = "流程实例的编号", required = true, example = "1024")
@NotEmpty(message = "流程实例的编号不能为空")
private String id;
@ApiModelProperty(value = "取消原因", required = true, example = "不请假了!")
@NotEmpty(message = "取消原因不能为空")
private String reason;
}

View File

@ -9,8 +9,6 @@ import java.util.Map;
@ApiModel("流程实例的创建 Request VO")
@Data
//@EqualsAndHashCode(callSuper = true)
//@ToString(callSuper = true)
public class BpmProcessInstanceCreateReqVO {
@ApiModelProperty(value = "流程定义的编号", required = true, example = "1024")

View File

@ -1,6 +1,44 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("流程实例的分页 Item Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmProcessInstanceMyPageReqVO extends PageParam {
@ApiModelProperty(value = "流程名称", example = "芋道")
private String name;
@ApiModelProperty(value = "流程定义的编号", example = "2048")
private String processDefinitionId;
@ApiModelProperty(value = "流程实例的状态", notes = "参见 bpm_process_instance_status", example = "1")
private Integer status;
@ApiModelProperty(value = "流程实例的结果", notes = "参见 bpm_process_instance_result", example = "2")
private Integer result;
@ApiModelProperty(value = "流程分类", notes = "参见 bpm_model_category 数据字典", example = "1")
private String category;
@ApiModelProperty(value = "开始的创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date beginCreateTime;
@ApiModelProperty(value = "结束的创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date endCreateTime;
}

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
@ApiModel("流程实例的分页 Item Response VO")
@Data
public class BpmProcessInstancePageItemRespVO {
@ApiModelProperty(value = "流程实例的编号", required = true, example = "1024")
private String id;
@ApiModelProperty(value = "流程名称", required = true, example = "芋道")
private String name;
@ApiModelProperty(value = "流程定义的编号", required = true, example = "2048")
private String processDefinitionId;
@ApiModelProperty(value = "流程分类", required = true, notes = "参见 bpm_model_category 数据字典", example = "1")
private String category;
@ApiModelProperty(value = "流程实例的状态", required = true, notes = "参见 bpm_process_instance_status", example = "1")
private Integer status;
@ApiModelProperty(value = "流程实例的结果", required = true, notes = "参见 bpm_process_instance_result", example = "2")
private Integer result;
@ApiModelProperty(value = "提交时间", required = true)
private Date createTime;
@ApiModelProperty(value = "结束时间", required = true)
private Date endTime;
/**
* 当前任务
*/
private List<Task> tasks;
@ApiModel("流程任务")
@Data
public static class Task {
@ApiModelProperty(value = "流程任务的编号", required = true, example = "1024")
private String id;
@ApiModelProperty(value = "任务名称", required = true, example = "芋道")
private String name;
}
}

View File

@ -0,0 +1,25 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Map;
@ApiModel("通过流程任务的 Request VO")
@Data
public class BpmTaskApproveReqVO {
@ApiModelProperty(value = "任务编号", required = true, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@ApiModelProperty(value = "审批意见", required = true, example = "不错不错!")
@NotEmpty(message = "审批意见不能为空")
private String comment;
}

View File

@ -0,0 +1,66 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ApiModel("流程任务的 Done 已完成的分页项 Response VO")
@Data
public class BpmTaskDonePageItemRespVO {
@ApiModelProperty(value = "任务编号", required = true, example = "1024")
private String id;
@ApiModelProperty(value = "任务名字", required = true, example = "芋道")
private String name;
@ApiModelProperty(value = "接收时间", required = true)
private Date claimTime;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
@ApiModelProperty(value = "结束时间", required = true)
private Date endTime;
@ApiModelProperty(value = "持续时间", required = true, example = "1000")
private Long durationInMillis;
@ApiModelProperty(value = "任务结果", required = true, notes = "参见 bpm_process_instance_result", example = "2")
private Integer result;
@ApiModelProperty(value = "审批建议", required = true, example = "不请假了!")
private String comment;
/**
* 所属流程实例
*/
private ProcessInstance processInstance;
@Data
@ApiModel("流程实例")
public static class ProcessInstance {
@ApiModelProperty(value = "流程实例编号", required = true, example = "1024")
private String id;
@ApiModelProperty(value = "流程实例名称", required = true, example = "芋道")
private String name;
@ApiModelProperty(value = "发起人的用户编号", required = true, example = "1024")
private Long startUserId;
@ApiModelProperty(value = "发起人的用户昵称", required = true, example = "芋艿")
private String startUserNickname;
@ApiModelProperty(value = "流程定义的编号", required = true, example = "2048")
private String processDefinitionId;
}
// 任务编号流程名称任务节点流程发起人接收时间审批时间耗时名称开始时间流程记录撤回
// 任务编号任务名称所属流程委托代办人流程发起人优先级审批操作审批意见耗时创建时间名称开始时间申请详情
// 任务编号任务名称流程名称流程发起人接收时间审批时间耗时名称接收时间详情TODO 撤回
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("流程任务的 Done 已办的分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmTaskDonePageReqVO extends PageParam {
@ApiModelProperty(value = "流程任务名", example = "芋道")
private String name;
@ApiModelProperty(value = "开始的创建收间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date beginCreateTime;
@ApiModelProperty(value = "结束的创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date endCreateTime;
}

View File

@ -0,0 +1,21 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
@ApiModel("不通过流程任务的 Request VO")
@Data
public class BpmTaskRejectReqVO {
@ApiModelProperty(value = "任务编号", required = true, example = "1024")
@NotEmpty(message = "任务编号不能为空")
private String id;
@ApiModelProperty(value = "审批意见", required = true, example = "不错不错!")
@NotEmpty(message = "审批意见不能为空")
private String comment;
}

View File

@ -0,0 +1,54 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ApiModel("流程任务的 Running 进行中的分页项 Response VO")
@Data
public class BpmTaskTodoPageItemRespVO {
@ApiModelProperty(value = "任务编号", required = true, example = "1024")
private String id;
@ApiModelProperty(value = "任务名字", required = true, example = "芋道")
private String name;
@ApiModelProperty(value = "接收时间", required = true)
private Date claimTime;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
@ApiModelProperty(value = "激活状态", required = true, example = "1", notes = "参见 SuspensionState 枚举")
private Integer suspensionState;
/**
* 所属流程实例
*/
private ProcessInstance processInstance;
@Data
@ApiModel("流程实例")
public static class ProcessInstance {
@ApiModelProperty(value = "流程实例编号", required = true, example = "1024")
private String id;
@ApiModelProperty(value = "流程实例名称", required = true, example = "芋道")
private String name;
@ApiModelProperty(value = "发起人的用户编号", required = true, example = "1024")
private Long startUserId;
@ApiModelProperty(value = "发起人的用户昵称", required = true, example = "芋艿")
private String startUserNickname;
@ApiModelProperty(value = "流程定义的编号", required = true, example = "2048")
private String processDefinitionId;
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("流程任务的 TODO 待办的分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class BpmTaskTodoPageReqVO extends PageParam {
@ApiModelProperty(value = "流程任务名", example = "芋道")
private String name;
@ApiModelProperty(value = "开始的创建收间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date beginCreateTime;
@ApiModelProperty(value = "结束的创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private Date endCreateTime;
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import lombok.Data;
import lombok.ToString;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import lombok.Data;
import lombok.ToString;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
package cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task;
import lombok.Data;
import lombok.ToString;

View File

@ -1,73 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
// TODO @jsonswagger validation 的注解后续要补全下哈可以等 workflow 基本写的差不多之后
@Api(tags = "工作流待办任务")
@RestController
@RequestMapping("/workflow/task")
public class TaskController {
@Resource
private BpmTaskService bpmTaskService;
@GetMapping("/todo/page")
@ApiOperation("获取待办任务分页")
public CommonResult<PageResult<TodoTaskRespVO>> getTodoTaskPage(@Valid TodoTaskPageReqVO pageVO) {
return success(bpmTaskService.getTodoTaskPage(pageVO));
}
@GetMapping("/claim")
@ApiOperation("签收任务")
public CommonResult<Boolean> claimTask(@RequestParam("id") String taskId) {
bpmTaskService.claimTask(taskId);
return success(true);
}
@PostMapping("/task-steps")
public CommonResult<TaskHandleVO> getTaskSteps(@RequestBody TaskQueryReqVO taskQuery) {
return success(bpmTaskService.getTaskSteps(taskQuery));
}
@PostMapping("/formKey")
public CommonResult<TodoTaskRespVO> getTaskFormKey(@RequestBody TaskQueryReqVO taskQuery) {
return success(bpmTaskService.getTaskFormKey(taskQuery));
}
@PostMapping("/complete")
public CommonResult<Boolean> complete(@RequestBody TaskReqVO taskReq) {
bpmTaskService.completeTask(taskReq);
return success(true);
}
@GetMapping("/process/history-steps")
public CommonResult<List<TaskStepVO>> getHistorySteps(@RequestParam("id") String processInstanceId) {
return success(bpmTaskService.getHistorySteps(processInstanceId));
}
/**
* 返回高亮的流转图SVG
* @param processInstanceId 流程Id
*/
@GetMapping("/process/highlight-img")
public void getHighlightImg(@RequestParam String processInstanceId, HttpServletResponse response) throws IOException {
FileResp fileResp = bpmTaskService.getHighlightImg(processInstanceId);
ServletUtils.writeAttachment(response, fileResp.getFileName(), fileResp.getFileByte());
}
}

View File

@ -1,17 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
import lombok.Data;
import lombok.ToString;
import java.util.Map;
@Data
@ToString
public class TaskReqVO {
private String taskId;
private Map<String,Object> variables;
private String comment;
}

View File

@ -1,16 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ApiModel("待办任务申请分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TodoTaskPageReqVO extends PageParam {
private String assignee;
}

View File

@ -1,37 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.ToString;
@ApiModel("待办任务 Response VO")
@Data
@ToString
public class TodoTaskRespVO {
// TODO @jasonswagger 注解这样接口文档才完整哈
private String id;
private String processInstanceId;
/**
* 1:未签收
* 2:已签收
*/
private Integer status;
private String processName;
private String processKey;
private String businessKey;
private String formKey;
}

View File

@ -1,7 +1,8 @@
package cn.iocoder.yudao.adminserver.modules.bpm.convert.definition;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionPageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionDO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@ -9,10 +10,14 @@ import org.activiti.engine.impl.persistence.entity.SuspensionState;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* Bpm 流程定义的 Convert
@ -25,17 +30,17 @@ public interface BpmDefinitionConvert {
BpmDefinitionConvert INSTANCE = Mappers.getMapper(BpmDefinitionConvert.class);
default List<BpmProcessDefinitionPageItemRespVO> convertList(List<ProcessDefinition> list, Map<String, Deployment> deploymentMap,
Map<String, BpmProcessDefinitionDO> processDefinitionDOMap, Map<Long, BpmFormDO> formMap) {
Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap, Map<Long, BpmFormDO> formMap) {
return CollectionUtils.convertList(list, definition -> {
Deployment deployment = definition.getDeploymentId() != null ? deploymentMap.get(definition.getDeploymentId()) : null;
BpmProcessDefinitionDO definitionDO = processDefinitionDOMap.get(definition.getId());
BpmProcessDefinitionExtDO definitionDO = processDefinitionDOMap.get(definition.getId());
BpmFormDO form = definitionDO != null ? formMap.get(definitionDO.getFormId()) : null;
return convert(definition, deployment, definitionDO, form);
});
}
default BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean, Deployment deployment,
BpmProcessDefinitionDO processDefinitionDO, BpmFormDO form) {
BpmProcessDefinitionExtDO processDefinitionDO, BpmFormDO form) {
BpmProcessDefinitionPageItemRespVO respVO = convert(bean);
respVO.setSuspensionState(bean.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode());
if (deployment != null) {
@ -53,6 +58,27 @@ public interface BpmDefinitionConvert {
BpmProcessDefinitionPageItemRespVO convert(ProcessDefinition bean);
BpmProcessDefinitionDO convert2(BpmDefinitionCreateReqDTO bean);
BpmProcessDefinitionExtDO convert2(BpmDefinitionCreateReqDTO bean);
default List<BpmProcessDefinitionRespVO> convertList3(List<ProcessDefinition> list,
Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap) {
return CollectionUtils.convertList(list, processDefinition -> {
BpmProcessDefinitionRespVO respVO = convert3(processDefinition);
BpmProcessDefinitionExtDO processDefinitionExtDO = processDefinitionDOMap.get(processDefinition.getId());
if (processDefinitionExtDO != null) {
respVO.setFormId(processDefinitionExtDO.getFormId());
}
return respVO;
});
}
@Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
BpmProcessDefinitionRespVO convert3(ProcessDefinition bean);
@Named("convertSuspendedToSuspensionState")
default Integer convertSuspendedToSuspensionState(boolean suspended) {
return suspended ? SuspensionState.SUSPENDED.getStateCode() :
SuspensionState.ACTIVE.getStateCode();
}
}

View File

@ -0,0 +1,61 @@
package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.sql.SQLXML;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* 流程实例 Convert
*
* @author 芋道源码
*/
@Mapper
public interface BpmProcessInstanceConvert {
BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);
@Mappings({
@Mapping(source = "instance.startUserId", target = "startUserId"),
@Mapping(source = "instance.id", target = "processInstanceId"),
@Mapping(source = "instance.startTime", target = "createTime"),
@Mapping(source = "definition.id", target = "processDefinitionId"),
@Mapping(source = "definition.name", target = "name"),
@Mapping(source = "definition.category", target = "category")
})
BpmProcessInstanceExtDO convert(ProcessInstance instance, ProcessDefinition definition);
default PageResult<BpmProcessInstancePageItemRespVO> convertPage(PageResult<BpmProcessInstanceExtDO> page,
Map<String, List<Task>> taskMap) {
List<BpmProcessInstancePageItemRespVO> list = convertList(page.getList());
list.forEach(respVO -> respVO.setTasks(convertList2(taskMap.get(respVO.getId()))));
return new PageResult<>(list, page.getTotal());
}
List<BpmProcessInstancePageItemRespVO> convertList(List<BpmProcessInstanceExtDO> list);
List<BpmProcessInstancePageItemRespVO.Task> convertList2(List<Task> tasks);
@Mapping(source = "processInstanceId", target = "id")
BpmProcessInstancePageItemRespVO convert(BpmProcessInstanceExtDO bean);
@Mappings({
@Mapping(source = "id", target = "processInstanceId"),
@Mapping(source = "startDate", target = "createTime"),
@Mapping(source = "initiator", target = "startUserId"),
@Mapping(source = "status", target = "status", ignore = true)
})
BpmProcessInstanceExtDO convert(org.activiti.api.process.model.ProcessInstance bean);
}

View File

@ -0,0 +1,103 @@
package cn.iocoder.yudao.adminserver.modules.bpm.convert.task;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskDonePageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.BpmTaskTodoPageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.TaskStepVO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.SuspensionState;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
import java.util.List;
import java.util.Map;
/**
* Bpm 任务 Convert
*
* @author 芋道源码
*/
@Mapper
public interface BpmTaskConvert {
BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);
@Mappings(value = {
@Mapping(source = "activityName", target = "stepName"),
@Mapping(source = "assignee", target = "assignee")
})
TaskStepVO convert(HistoricActivityInstance instance);
default List<BpmTaskTodoPageItemRespVO> convertList(List<Task> tasks, Map<String, ProcessInstance> processInstanceMap,
Map<Long, SysUserDO> userMap) {
return CollectionUtils.convertList(tasks, task -> {
ProcessInstance processInstance = processInstanceMap.get(task.getProcessInstanceId());
return convert(task, processInstance, userMap.get(Long.valueOf(processInstance.getStartUserId())));
});
}
@Mappings({
@Mapping(source = "task.id", target = "id"),
@Mapping(source = "task.name", target = "name"),
@Mapping(source = "task.claimTime", target = "claimTime"),
@Mapping(source = "task.createTime", target = "createTime"),
@Mapping(source = "task.suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState"),
@Mapping(source = "processInstance.id", target = "processInstance.id"),
@Mapping(source = "processInstance.name", target = "processInstance.name"),
@Mapping(source = "processInstance.startUserId", target = "processInstance.startUserId"),
@Mapping(source = "processInstance.processDefinitionId", target = "processInstance.processDefinitionId"),
@Mapping(source = "user.nickname", target = "processInstance.startUserNickname")
})
BpmTaskTodoPageItemRespVO convert(Task task, ProcessInstance processInstance, SysUserDO user);
@Named("convertSuspendedToSuspensionState")
default Integer convertSuspendedToSuspensionState(boolean suspended) {
return suspended ? SuspensionState.SUSPENDED.getStateCode() :
SuspensionState.ACTIVE.getStateCode();
}
default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks, Map<String, BpmTaskExtDO> bpmTaskExtDOMap,
Map<String, HistoricProcessInstance> historicProcessInstanceMap,
Map<Long, SysUserDO> userMap) {
return CollectionUtils.convertList(tasks, task -> {
BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId());
HistoricProcessInstance processInstance = historicProcessInstanceMap.get(task.getProcessInstanceId());
SysUserDO userDO = userMap.get(Long.valueOf(processInstance.getStartUserId()));
return convert(task, taskExtDO, processInstance, userDO);
});
}
@Mappings({
@Mapping(source = "task.id", target = "id"),
@Mapping(source = "task.name", target = "name"),
@Mapping(source = "task.claimTime", target = "claimTime"),
@Mapping(source = "task.createTime", target = "createTime"),
@Mapping(source = "task.endTime", target = "endTime"),
@Mapping(source = "task.durationInMillis", target = "durationInMillis"),
@Mapping(source = "taskExtDO.result", target = "result"),
@Mapping(source = "taskExtDO.comment", target = "comment"),
@Mapping(source = "processInstance.id", target = "processInstance.id"),
@Mapping(source = "processInstance.name", target = "processInstance.name"),
@Mapping(source = "processInstance.startUserId", target = "processInstance.startUserId"),
@Mapping(source = "processInstance.processDefinitionId", target = "processInstance.processDefinitionId"),
@Mapping(source = "user.nickname", target = "processInstance.startUserNickname")
})
BpmTaskDonePageItemRespVO convert(HistoricTaskInstance task, BpmTaskExtDO taskExtDO, HistoricProcessInstance processInstance, SysUserDO user);
@Mappings({
@Mapping(source = "id", target = "taskId"),
@Mapping(source = "assignee", target = "assigneeUserId"),
@Mapping(source = "createdDate", target = "createTime")
})
BpmTaskExtDO convert(org.activiti.api.task.model.Task bean);
}

View File

@ -1,44 +0,0 @@
package cn.iocoder.yudao.adminserver.modules.bpm.convert.workflow;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.TaskStepVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.TodoTaskRespVO;
import org.activiti.api.task.model.Task;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.Named;
import org.mapstruct.factory.Mappers;
@Mapper
public interface TaskConvert {
TaskConvert INSTANCE = Mappers.getMapper(TaskConvert.class);
@Mappings(value = {
@Mapping(source = "task.id", target = "id"),
@Mapping(source = "task.businessKey", target = "businessKey"),
@Mapping(source = "task.assignee", target = "status",qualifiedByName = "convertAssigneeToStatus"),
@Mapping(source = "definition.name", target = "processName"),
@Mapping(source = "definition.key", target = "processKey"),
@Mapping(source = "definition.id", target = "processInstanceId")
})
TodoTaskRespVO convert(Task task, ProcessDefinition definition);
@Mappings(value = {
@Mapping(source = "assignee", target = "status",qualifiedByName = "convertAssigneeToStatus")
})
TodoTaskRespVO convert(Task task);
@Named("convertAssigneeToStatus")
default Integer convertAssigneeToStatus(String assignee) {
//TODO 不应该通过 assignee 定义状态 需要定义更多的状态
return assignee == null ? 1 : 2;
}
@Mappings(value = {
@Mapping(source = "activityName", target = "stepName"),
@Mapping(source = "assignee", target = "assignee")
})
TaskStepVO convert(HistoricActivityInstance instance);
}

View File

@ -9,18 +9,18 @@ import org.activiti.engine.repository.ProcessDefinition;
/**
* Bpm 流程定义的拓展表
* 主要解决 主要进行 Activiti {@link ProcessDefinition} 不支持拓展字段所以新建拓展表
* 主要解决 Activiti {@link ProcessDefinition} 不支持拓展字段所以新建拓展表
*
* @author 芋道源码
*/
@TableName(value = "bpm_process_definition", autoResultMap = true)
@TableName(value = "bpm_process_definition_ext", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BpmProcessDefinitionDO extends BaseDO {
public class BpmProcessDefinitionExtDO extends BaseDO {
/**
* 编号

View File

@ -0,0 +1,81 @@
package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import java.util.Date;
/**
* Bpm 流程实例的拓展表
* 主要解决 Activiti {@link ProcessInstance} {@link HistoricProcessInstance} 不支持拓展字段所以新建拓展表
*
* @author 芋道源码
*/
@TableName(value = "bpm_process_instance_ext", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
//@Builder
//@NoArgsConstructor
//@AllArgsConstructor
public class BpmProcessInstanceExtDO extends BaseDO {
/**
* 发起流程的用户编号
*
* 冗余 {@link HistoricProcessInstance#getStartUserId()}
*/
private Long startUserId;
/**
* 流程实例的名字
*
* 冗余 {@link ProcessInstance#getName()} 为了筛选
*/
private String name;
/**
* 流程实例的编号
*
* 关联 {@link ProcessInstance#getId()}
*/
private String processInstanceId;
/**
* 流程定义的编号
*
* 关联 {@link ProcessDefinition#getId()}
*/
private String processDefinitionId;
/**
* 流程分类
*
* 冗余 {@link ProcessDefinition#getCategory()}
* 数据字典 bpm_model_category
*/
private String category;
/**
* 流程实例的状态
*
* 枚举 {@link BpmProcessInstanceStatusEnum}
*/
private Integer status;
/**
* 流程实例的结果
*
* 枚举 {@link BpmProcessInstanceResultEnum}
*/
private Integer result;
/**
* 结束时间
*
* 冗余 {@link HistoricProcessInstance#getEndTime()}
*/
private Date endTime;
}

View File

@ -0,0 +1,79 @@
package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import java.util.Date;
/**
* Bpm 流程任务的拓展表
* 主要解决 Activiti {@link Task} {@link HistoricTaskInstance} 不支持拓展字段所以新建拓展表
*
* @author 芋道源码
*/
@TableName(value = "bpm_task_ext", autoResultMap = true)
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
//@Builder
//@NoArgsConstructor
//@AllArgsConstructor
public class BpmTaskExtDO extends BaseDO {
/**
* 任务的审批人
*
* 冗余 {@link Task#getAssignee()}
*/
private Long assigneeUserId;
/**
* 任务的名字
*
* 冗余 {@link Task#getName()} 为了筛选
*/
private String name;
/**
* 任务的编号
*
* 关联 {@link Task#getId()}
*/
private String taskId;
/**
* 任务的结果
*
* 枚举 {@link BpmProcessInstanceResultEnum}
*/
private Integer result;
/**
* 审批建议
*/
private String comment;
/**
* 任务的结束时间
*
* 冗余 {@link HistoricTaskInstance#getEndTime()}
*/
private Date endTime;
/**
* 流程实例的编号
*
* 关联 {@link ProcessInstance#getId()}
*/
private String processInstanceId;
/**
* 流程定义的编号
*
* 关联 {@link ProcessDefinition#getId()}
*/
private String processDefinitionId;
}

View File

@ -1,4 +0,0 @@
/**
* TODO 芋艿工作流创建后的定义
*/
package cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task;

View File

@ -1,6 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@ -9,10 +9,10 @@ import java.util.Collection;
import java.util.List;
@Mapper
public interface BpmProcessDefinitionMapper extends BaseMapper<BpmProcessDefinitionDO> {
public interface BpmProcessDefinitionExtMapper extends BaseMapper<BpmProcessDefinitionExtDO> {
default List<BpmProcessDefinitionDO> selectListByProcessDefinitionIds(Collection<String> processDefinitionIds) {
return selectList(new QueryWrapper<BpmProcessDefinitionDO>().in("process_definition_id", processDefinitionIds));
default List<BpmProcessDefinitionExtDO> selectListByProcessDefinitionIds(Collection<String> processDefinitionIds) {
return selectList(new QueryWrapper<BpmProcessDefinitionExtDO>().in("process_definition_id", processDefinitionIds));
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.task;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceMyPageReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
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 com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BpmProcessInstanceExtMapper extends BaseMapperX<BpmProcessInstanceExtDO> {
default PageResult<BpmProcessInstanceExtDO> selectPage(Long userId, BpmProcessInstanceMyPageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<BpmProcessInstanceExtDO>()
.eqIfPresent("start_user_id", userId)
.likeIfPresent("name", reqVO.getName())
.eqIfPresent("process_definition_id", reqVO.getProcessDefinitionId())
.eqIfPresent("category", reqVO.getCategory())
.eqIfPresent("status", reqVO.getStatus())
.eqIfPresent("result", reqVO.getResult())
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("id"));
}
default void updateByProcessInstanceId(BpmProcessInstanceExtDO updateObj) {
update(updateObj, new QueryWrapper<BpmProcessInstanceExtDO>()
.eq("process_instance_id", updateObj.getProcessInstanceId()));
}
}

View File

@ -0,0 +1,22 @@
package cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.task;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
@Mapper
public interface BpmTaskExtMapper extends BaseMapperX<BpmTaskExtDO> {
default void updateByTaskId(BpmTaskExtDO entity) {
update(entity, new QueryWrapper<BpmTaskExtDO>().eq("task_id", entity.getTaskId()));
}
default List<BpmTaskExtDO> selectListByTaskIds(Collection<String> taskIds) {
return selectList("task_id", taskIds);
}
}

View File

@ -10,7 +10,6 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
public interface BpmErrorCodeConstants {
// ========== 通用流程处理 模块 1-009-000-000 ==========
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009000001, "流程实例不存在");
ErrorCode HIGHLIGHT_IMG_ERROR = new ErrorCode(1009000002, "获取高亮流程图异常");
// ========== OA 流程模块 1-009-001-000 ==========
@ -34,7 +33,12 @@ public interface BpmErrorCodeConstants {
ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1009003002, "流程定义处于挂起状态");
// ========== 流程实例 1-009-004-000 ==========
ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1009004000, "流程实例不存在");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1009004001, "流程取消失败,流程不处于运行中");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1009004002, "流程取消失败,该流程不是你发起的");
// ========== 流程实例 1-009-005-000 ==========
ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1009004000, "审批任务失败,原因:该任务不处于未审批");
// ========== 动态表单模块 1-009-010-000 ==========
ErrorCode FORM_NOT_EXISTS = new ErrorCode(1009010000, "动态表单不存在");

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.adminserver.modules.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程实例的删除原因
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum BpmProcessInstanceDeleteReasonEnum {
REJECT_TASK("驳回任务");
private final String reason;
}

View File

@ -0,0 +1,29 @@
package cn.iocoder.yudao.adminserver.modules.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程实例的结果
*
* @author jason
*/
@Getter
@AllArgsConstructor
public enum BpmProcessInstanceResultEnum {
PROCESS(1, "处理中"),
APPROVE(2, "通过"),
REJECT(3, "不通过"),
CANCEL(4, "已取消");
/**
* 结果
*/
private final Integer result;
/**
* 描述
*/
private final String desc;
}

View File

@ -1,29 +1,27 @@
package cn.iocoder.yudao.adminserver.modules.bpm.enums;
package cn.iocoder.yudao.adminserver.modules.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 流程状态
* 流程实例的状态
*
* @author 芋道源码
*/
@Getter
@AllArgsConstructor
public enum FlowStatusEnum {
public enum BpmProcessInstanceStatusEnum {
HANDLE(1, "处理中"),
PASS(2, "审批通过"),
REJECTED(3, "审批不通过");
RUNNING(1, "进行中"),
FINISH(2, "已完成");
/**
* 状态
*/
private final Integer status;
/**
* 描述
*/
private final String desc;
}

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.listener.BpmTackActivitiEventListener;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.Collections;
/**
* BPM 模块的 Activiti 配置类
*/
@Configuration
public class BpmActivitiConfiguration implements ProcessEngineConfigurationConfigurer {
@Resource
private BpmTackActivitiEventListener taskActivitiEventListener;
@Override
public void configure(SpringProcessEngineConfiguration configuration) {
// 注册监听器例如说 BpmActivitiEventListener
configuration.setEventListeners(Collections.singletonList(taskActivitiEventListener));
}
}

View File

@ -0,0 +1,6 @@
/**
* 属于 bpm 模块的 framework 封装
*
* @author 芋道源码
*/
package cn.iocoder.yudao.adminserver.modules.bpm.framework;

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.definition;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionListReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionPageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionPageReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@ -30,6 +32,14 @@ public interface BpmProcessDefinitionService {
*/
PageResult<BpmProcessDefinitionPageItemRespVO> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO);
/**
* 获得流程定义列表
*
* @param listReqVO 列表入参
* @return 流程定义列表
*/
List<BpmProcessDefinitionRespVO> getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO);
/**
* 获得流程定义对应的 BPMN XML
*
@ -54,6 +64,16 @@ public interface BpmProcessDefinitionService {
*/
ProcessDefinition getProcessDefinition(String id);
/**
* 获得编号对应的 ProcessDefinition
*
* 相比 {@link #getProcessDefinition(String)} 方法category 的取值是正确
*
* @param id 编号
* @return 流程定义
*/
ProcessDefinition getProcessDefinition2(String id);
/**
* 获得 id 对应的 Deployment
*

View File

@ -2,12 +2,14 @@ package cn.iocoder.yudao.adminserver.modules.bpm.service.definition.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionListReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionPageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionPageReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.BpmProcessDefinitionRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.convert.definition.BpmDefinitionConvert;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.form.BpmFormDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition.BpmProcessDefinitionMapper;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition.BpmProcessDefinitionExtMapper;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.dto.BpmDefinitionCreateReqDTO;
import cn.iocoder.yudao.adminserver.modules.bpm.service.form.BpmFormService;
@ -56,7 +58,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
private BpmFormService bpmFormService;
@Resource
private BpmProcessDefinitionMapper processDefinitionMapper;
private BpmProcessDefinitionExtMapper processDefinitionMapper;
@Override
public PageResult<BpmProcessDefinitionPageItemRespVO> getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) {
@ -77,14 +79,13 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
Map<String, Deployment> deploymentMap = getDeploymentMap(deploymentIds);
// 获得 BpmProcessDefinitionDO Map
List<BpmProcessDefinitionDO> processDefinitionDOs = Collections.emptyList();
processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
List<BpmProcessDefinitionExtDO> processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
convertList(processDefinitions, ProcessDefinition::getId));
Map<String, BpmProcessDefinitionDO> processDefinitionDOMap = CollectionUtils.convertMap(processDefinitionDOs,
BpmProcessDefinitionDO::getProcessDefinitionId);
Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = CollectionUtils.convertMap(processDefinitionDOs,
BpmProcessDefinitionExtDO::getProcessDefinitionId);
// 获得 Form Map
Set<Long> formIds = CollectionUtils.convertSet(processDefinitionDOs, BpmProcessDefinitionDO::getFormId);
Set<Long> formIds = CollectionUtils.convertSet(processDefinitionDOs, BpmProcessDefinitionExtDO::getFormId);
Map<Long, BpmFormDO> formMap = bpmFormService.getFormMap(formIds);
// 拼接结果
@ -93,6 +94,27 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
processDefinitionDOMap, formMap), definitionCount);
}
@Override
public List<BpmProcessDefinitionRespVO> getProcessDefinitionList(BpmProcessDefinitionListReqVO listReqVO) {
// 拼接查询条件
ProcessDefinitionQuery definitionQuery = repositoryService.createProcessDefinitionQuery();
if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), listReqVO.getSuspensionState())) {
definitionQuery.suspended();
} else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), listReqVO.getSuspensionState())) {
definitionQuery.active();
}
// 执行查询
List<ProcessDefinition> processDefinitions = definitionQuery.list();
// 获得 BpmProcessDefinitionDO Map
List<BpmProcessDefinitionExtDO> processDefinitionDOs = processDefinitionMapper.selectListByProcessDefinitionIds(
convertList(processDefinitions, ProcessDefinition::getId));
Map<String, BpmProcessDefinitionExtDO> processDefinitionDOMap = CollectionUtils.convertMap(processDefinitionDOs,
BpmProcessDefinitionExtDO::getProcessDefinitionId);
// 执行查询并返回
return BpmDefinitionConvert.INSTANCE.convertList3(processDefinitions, processDefinitionDOMap);
}
@Override
public String getProcessDefinitionBpmnXML(String id) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
@ -113,6 +135,11 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
return repositoryService.getProcessDefinition(id);
}
@Override
public ProcessDefinition getProcessDefinition2(String id) {
return repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
}
@Override
public Deployment getDeployment(String id) {
if (StrUtil.isEmpty(id)) {
@ -169,7 +196,7 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ
}
// 插入拓展表
BpmProcessDefinitionDO definitionDO = BpmDefinitionConvert.INSTANCE.convert2(createReqDTO)
BpmProcessDefinitionExtDO definitionDO = BpmDefinitionConvert.INSTANCE.convert2(createReqDTO)
.setProcessDefinitionId(definition.getId());
processDefinitionMapper.insert(definitionDO);
return definition.getId();

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.adminserver.modules.bpm.service.oa;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.OALeaveDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.oa.OALeaveMapper;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.FlowStatusEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.springframework.stereotype.Component;
@ -29,9 +29,9 @@ public class LeaveApplyEndProcessor implements ExecutionListener {
OALeaveDO updateDo = new OALeaveDO();
updateDo.setId(Long.valueOf(businessKey));
if (Objects.equals(approved, true)) {
updateDo.setStatus(FlowStatusEnum.PASS.getStatus());
updateDo.setStatus(BpmProcessInstanceResultEnum.APPROVE.getResult());
} else {
updateDo.setStatus(FlowStatusEnum.REJECTED.getStatus());
updateDo.setStatus(BpmProcessInstanceResultEnum.REJECT.getResult());
}
leaveMapper.updateById(updateDo);

View File

@ -6,7 +6,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.oa.vo.*;
import cn.iocoder.yudao.adminserver.modules.bpm.convert.oa.OALeaveConvert;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.leave.OALeaveDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.oa.OALeaveMapper;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.FlowStatusEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.service.oa.OALeaveService;
import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysPostDO;
import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.dept.SysPostMapper;
@ -62,7 +62,7 @@ public class OALeaveServiceImpl implements OALeaveService {
public Long createLeave(OALeaveCreateReqVO createReqVO) {
// 插入 OA 请假单
OALeaveDO leave = OALeaveConvert.INSTANCE.convert(createReqVO);
leave.setStatus(FlowStatusEnum.HANDLE.getStatus());
leave.setStatus(BpmProcessInstanceResultEnum.PROCESS.getResult());
// TODO @jason应该是存储 userId
leave.setUserId(SecurityFrameworkUtils.getLoginUser().getUsername());
leaveMapper.insert(leave);

View File

@ -1,8 +1,20 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCancelReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceMyPageReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.runtime.ProcessInstance;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 流程实例 Service 接口
@ -20,4 +32,120 @@ public interface BpmProcessInstanceService {
*/
String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO);
/**
* 取消流程实例
*
* @param userId 用户编号
* @param cancelReqVO 取消信息
*/
void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO);
/**
* 删除流程实例
*
* @param id 流程编号
* @param reason 删除原因可选 {@link BpmProcessInstanceDeleteReasonEnum}
*/
@Deprecated
void deleteProcessInstance(String id, String reason);
/**
* 更新流程实例的结果
* 1. 如果更新为已拒绝时会进行任务的删除
*
* @param id 流程编号
* @param result 结果{@link BpmProcessInstanceResultEnum}
*/
void updateProcessInstanceResult(String id, Integer result);
/**
* 获得流程实例的分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程实例的分页
*/
PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
@Valid BpmProcessInstanceMyPageReqVO pageReqVO);
/**
* 获得流程实例
*
* @param id 流程实例的编号
* @return 流程实例
*/
ProcessInstance getProcessInstance(String id);
/**
* 获得流程实例列表
*
* @param ids 流程实例的编号集合
* @return 流程实例列表
*/
List<ProcessInstance> getProcessInstances(Set<String> ids);
/**
* 获得流程实例 Map
*
* @param ids 流程实例的编号集合
* @return 流程实例列表 Map
*/
default Map<String, ProcessInstance> getProcessInstanceMap(Set<String> ids) {
return CollectionUtils.convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId);
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
HistoricProcessInstance getHistoricProcessInstance(String id);
/**
* 获得历史的流程实例列表
*
* @param ids 流程实例的编号集合
* @return 历史的流程实例列表
*/
List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids);
/**
* 获得历史的流程实例 Map
*
* @param ids 流程实例的编号集合
* @return 历史的流程实例列表 Map
*/
default Map<String, HistoricProcessInstance> getHistoricProcessInstanceMap(Set<String> ids) {
return CollectionUtils.convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId);
}
/**
* 创建 ProcessInstance 拓展记录
*
* @param instance 流程任务
*/
void createProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance);
/**
* 更新 ProcessInstance 拓展记录
*
* @param instance 流程任务
*/
void updateProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance);
/**
* 更新 ProcessInstance 拓展记录为取消
*
* @param instance 流程任务
*/
void updateProcessInstanceExtCancel(org.activiti.api.process.model.ProcessInstance instance);
/**
* 更新 ProcessInstance 拓展记录为完成
*
* @param instance 流程任务
*/
void updateProcessInstanceExtComplete(org.activiti.api.process.model.ProcessInstance instance);
}

View File

@ -1,57 +1,138 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.task;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import org.activiti.engine.task.Task;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
/**
* 工作流用户任务服务接口
* 流程任务 Service 接口
*
* @author jason
* @author 芋道源码
*/
public interface BpmTaskService {
/**
* 获得流程任务列表
*
* @param processInstanceId 流程实例的编号
* @return 流程任务列表
*/
List<Task> getTasksByProcessInstanceId(String processInstanceId);
/**
* 获取当前用户的待办任务 分页
* 获得流程任务列表
*
* @param processInstanceIds 流程实例的编号数组
* @return 流程任务列表
*/
PageResult<TodoTaskRespVO> getTodoTaskPage(TodoTaskPageReqVO pageReqVO);
List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds);
/**
* 签收任务
* @param taskId 用户任务id
* 获得流程任务 Map
*
* @param processInstanceIds 流程实例的编号数组
* @return 流程任务 Map
*/
void claimTask(String taskId);
default Map<String, List<Task>> getTaskMapByProcessInstanceIds(List<String> processInstanceIds) {
return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds),
Task::getProcessInstanceId);
}
/**
* 工作流完成 userTask, 完成用户任务 一般传入参数 1是否同意variables). 2. 评论(comment)
* variables 变量名 评论 由前台传入
* @param taskReq 任务参数
* 获得待办的流程任务分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程任务分页
*/
void completeTask(TaskReqVO taskReq);
PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageReqVO);
/**
* 获得已办的流程任务分页
*
* @param userId 用户编号
* @param pageReqVO 分页请求
* @return 流程任务分页
*/
PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageReqVO);
/**
* 将流程任务分配给指定用户
*
* @param id 流程任务编号
* @param userId 用户编号
*/
void updateTaskAssign(String id, Long userId);
/**
* 通过任务
*
* @param reqVO 通过请求
*/
void approveTask(@Valid BpmTaskApproveReqVO reqVO);
/**
* 不通过任务
*
* @param reqVO 不通过请求
*/
void rejectTask(@Valid BpmTaskRejectReqVO reqVO);
/**
* 根据任务id, 查询已经完成的用户任务未完成的用户任务
* @param taskQuery 查询参数 一般 taskId
*/
@Deprecated
TaskHandleVO getTaskSteps(TaskQueryReqVO taskQuery);
/**
* 根据流程实例id, 查询历史用户任务包括已完成未完成
* @param processInstanceId 流程实例id
*/
@Deprecated
List<TaskStepVO> getHistorySteps(String processInstanceId);
/**
* 获取用户任务的 formKey, 对应外置表单 需要根据formKey 对应业务表单
* @param taskQuery 查询参数 ,一般taskId
*/
TodoTaskRespVO getTaskFormKey(TaskQueryReqVO taskQuery);
/**
* 返回高亮的流转进程
* @param processInstanceId 实例Id
* @return {@link FileResp} 返回文件
*/
FileResp getHighlightImg(String processInstanceId);
// ========== Task 拓展表相关 ==========
/**
* 创建 Task 拓展记录
*
* @param task 任务实体
*/
void createTaskExt(org.activiti.api.task.model.Task task);
/**
* 更新 Task 拓展记录
*
* @param task 任务实体
*/
void updateTaskExt(org.activiti.api.task.model.Task task);
/**
* 更新 Task 拓展记录为取消
*
* @param task 任务实体
*/
void updateTaskExtCancel(org.activiti.api.task.model.Task task);
/**
* 更新 Task 拓展记录为完成
*
* @param task 任务实体
*/
void updateTaskExtComplete(org.activiti.api.task.model.Task task);
}

View File

@ -1,30 +1,39 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.task.impl;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCancelReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceCreateReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstanceMyPageReqVO;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.instance.BpmProcessInstancePageItemRespVO;
import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmProcessInstanceConvert;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.task.BpmProcessInstanceExtMapper;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstanceQuery;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Map;
import java.util.*;
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_IS_SUSPENDED;
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS;
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/**
* 流程实例 Service 实现类
@ -43,21 +52,24 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
@Slf4j
public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService {
@Resource
private RepositoryService repositoryService;
@Resource
private RuntimeService runtimeService;
@Resource
private TaskService taskService;
@Resource
private HistoryService historyService;
@Resource
private SysUserService userService;
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Resource
private BpmProcessDefinitionService processDefinitionService;
@Resource
private BpmProcessInstanceExtMapper processInstanceExtMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public String createProcessInstance(Long userId, BpmProcessInstanceCreateReqVO createReqVO) {
// 校验流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId());
@ -68,33 +80,141 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
throw exception(PROCESS_DEFINITION_IS_SUSPENDED);
}
// 设置流程发起人
Authentication.setAuthenticatedUserId(String.valueOf(userId));
// 创建流程实例
Map<String, Object> variables = createReqVO.getVariables();
variables.put("INITIATOR", userId); // TODO 芋艿初始化人员
ProcessInstance instance = runtimeService.startProcessInstanceById(createReqVO.getProcessDefinitionId(), variables);
// 设置流程名字
runtimeService.setProcessInstanceName(instance.getId(), definition.getName());
// TODO 芋艿临时使用, 保证分配
List<Task> tasks = taskService.getTasksByProcessInstanceId(instance.getId());
tasks.forEach(task -> taskService.updateTaskAssign(task.getId(), userId));
// 添加初始的评论 TODO 芋艿在思考下
Task task = taskService.createTaskQuery().processInstanceId(instance.getId()).singleResult();
if (task != null) {
SysUserDO user = userService.getUser(userId);
Assert.notNull(user, "用户({})不存在", userId);
String type = "normal";
taskService.addComment(task.getId(), instance.getProcessInstanceId(), type,
String.format("%s 发起流程申请", user.getNickname()));
// TODO 芋艿应该不用下面两个步骤
// taskService.setAssignee(task.getId(), String.valueOf(userId));
// taskService.complete(task.getId(), variables);
}
// Task task = taskService.createTaskQuery().processInstanceId(instance.getId()).singleResult();
// if (task != null) {
// SysUserDO user = userService.getUser(userId);
// Assert.notNull(user, "用户({})不存在", userId);
// String type = "normal";
// taskService.addComment(task.getId(), instance.getProcessInstanceId(), type,
// String.format("%s 发起流程申请", user.getNickname()));
// // TODO 芋艿应该不用下面两个步骤
//// taskService.setAssignee(task.getId(), String.valueOf(userId));
//// taskService.complete(task.getId(), variables);
// }
return instance.getId();
}
public void getMyProcessInstancePage(Long userId) {
HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
.startedBy(String.valueOf(userId)) // 发起人是自己
.orderByProcessInstanceStartTime().desc(); // 按照发起时间倒序
@Override
@Transactional(rollbackFor = Exception.class)
public void cancelProcessInstance(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) {
// 校验流程实例存在
ProcessInstance instance = getProcessInstance(cancelReqVO.getId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS);
}
// 只能取消自己的
if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) {
throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF);
}
// 通过删除流程实例实现流程实例的取消
runtimeService.deleteProcessInstance(cancelReqVO.getId(), cancelReqVO.getReason());
}
@Override
public void deleteProcessInstance(String id, String reason) {
runtimeService.deleteProcessInstance(id, reason);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateProcessInstanceResult(String id, Integer result) {
// 删除流程实例以实现驳回任务时取消整个审批流程
if (Objects.equals(result, BpmProcessInstanceResultEnum.REJECT.getResult())) {
deleteProcessInstance(id, BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.getReason());
}
// 更新 status + result
processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(id)
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()).setResult(result));
}
@Override
public PageResult<BpmProcessInstancePageItemRespVO> getMyProcessInstancePage(Long userId,
BpmProcessInstanceMyPageReqVO pageReqVO) {
// 通过 BpmProcessInstanceExtDO 先查询到对应的分页
PageResult<BpmProcessInstanceExtDO> pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
return new PageResult<>(pageResult.getTotal());
}
// 获得流程 Task Map
List<String> processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId);
Map<String, List<Task>> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds);
// 转换返回
return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap);
}
@Override
public List<ProcessInstance> getProcessInstances(Set<String> ids) {
return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public ProcessInstance getProcessInstance(String id) {
return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
}
/**
* 获得历史的流程实例
*
* @param id 流程实例的编号
* @return 历史的流程实例
*/
@Override
public HistoricProcessInstance getHistoricProcessInstance(String id) {
return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
}
@Override
public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) {
return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list();
}
@Override
public void createProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance) {
// 获得流程定义
ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId());
// 插入 BpmProcessInstanceExtDO 对象
BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance)
.setCategory(definition.getCategory())
.setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus())
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
processInstanceExtMapper.insert(instanceExtDO);
}
@Override
public void updateProcessInstanceExt(org.activiti.api.process.model.ProcessInstance instance) {
BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance);
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
}
@Override
public void updateProcessInstanceExtCancel(org.activiti.api.process.model.ProcessInstance instance) {
BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance)
.setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
}
@Override
public void updateProcessInstanceExtComplete(org.activiti.api.process.model.ProcessInstance instance) {
BpmProcessInstanceExtDO instanceExtDO = BpmProcessInstanceConvert.INSTANCE.convert(instance)
.setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime所以这里设置
.setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全说明审批通过
processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO);
}
}

View File

@ -2,19 +2,20 @@ package cn.iocoder.yudao.adminserver.modules.bpm.service.task.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.workflow.vo.*;
import cn.iocoder.yudao.adminserver.modules.bpm.convert.workflow.TaskConvert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.bpm.controller.task.vo.task.*;
import cn.iocoder.yudao.adminserver.modules.bpm.convert.task.BpmTaskConvert;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.task.BpmTaskExtMapper;
import cn.iocoder.yudao.adminserver.modules.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.common.util.object.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.ClaimTaskPayloadBuilder;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.bpmn.constants.BpmnXMLConstants;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
@ -22,100 +23,210 @@ import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.*;
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.HIGHLIGHT_IMG_ERROR;
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS;
import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
* 流程任务 Service 实现类
*
* @author jason
* @author 芋道源码
*/
@Slf4j
@Service
public class BpmTaskServiceImpl implements BpmTaskService {
@Resource
private TaskRuntime taskRuntime;
private TaskService taskService;
@Resource
private org.activiti.engine.TaskService activitiTaskService;
private RuntimeService runtimeService;
@Resource
private HistoryService historyService;
@Resource
private RepositoryService repositoryService;
@Resource
private RuntimeService runtimeService;
@Resource
private ProcessDiagramGenerator processDiagramGenerator;
@Override
public PageResult<TodoTaskRespVO> getTodoTaskPage(TodoTaskPageReqVO pageReqVO) {
// TODO @jason封装一个方法用于转换成 activiti 的分页对象
final Pageable pageable = Pageable.of((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize(), pageReqVO.getPageSize());
Page<Task> pageTasks = taskRuntime.tasks(pageable);
int totalItems = pageTasks.getTotalItems();
List<Task> tasks = pageTasks.getContent();
final List<TodoTaskRespVO> respVOList = tasks.stream().map(task -> {
ProcessDefinition definition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
return TaskConvert.INSTANCE.convert(task, definition);
}).collect(Collectors.toList());
return new PageResult<>(respVOList, (long)totalItems);
}
@Resource
private SysUserService userService;
@Resource
@Lazy // 解决循环依赖
private BpmProcessInstanceService processInstanceService;
@Resource
private BpmTaskExtMapper taskExtMapper;
@Override
public void claimTask(String taskId) {
taskRuntime.claim(new ClaimTaskPayloadBuilder()
.withTaskId(taskId)
.withAssignee(SecurityFrameworkUtils.getLoginUser().getUsername())
.build());
public List<Task> getTasksByProcessInstanceId(String processInstanceId) {
return taskService.createTaskQuery().processInstanceId(processInstanceId).list();
}
@Override
@Transactional
public void completeTask(TaskReqVO taskReq) {
final Task task = taskRuntime.task(taskReq.getTaskId());
activitiTaskService.addComment(taskReq.getTaskId(), task.getProcessInstanceId(), taskReq.getComment());
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(taskReq.getTaskId())
.withVariables(taskReq.getVariables())
.build());
public List<Task> getTasksByProcessInstanceIds(List<String> processInstanceIds) {
if (CollUtil.isEmpty(processInstanceIds)) {
return Collections.emptyList();
}
return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list();
}
@Override
public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) {
// 查询待办任务
TaskQuery taskQuery = taskService.createTaskQuery()
.taskAssignee(String.valueOf(userId)) // 分配给自己
.orderByTaskCreateTime().desc(); // 创建时间倒序
if (StrUtil.isNotBlank(pageVO.getName())) {
taskQuery.taskNameLike("%" + pageVO.getName() + "%");
}
if (pageVO.getBeginCreateTime() != null) {
taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime());
}
if (pageVO.getEndCreateTime() != null) {
taskQuery.taskCreatedBefore(pageVO.getEndCreateTime());
}
// 执行查询
List<Task> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
if (CollUtil.isEmpty(tasks)) {
return PageResult.empty(taskQuery.count());
}
// 获得 ProcessInstance Map
Map<String, ProcessInstance> processInstanceMap = processInstanceService.getProcessInstanceMap(
convertSet(tasks, Task::getProcessInstanceId));
// 获得 User Map
Map<Long, SysUserDO> userMap = userService.getUserMap(
convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
// 拼接结果
return new PageResult<>(BpmTaskConvert.INSTANCE.convertList(tasks, processInstanceMap, userMap),
taskQuery.count());
}
@Override
public PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) {
// 查询已办任务
HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery()
.finished() // 已完成
.taskAssignee(String.valueOf(userId)) // 分配给自己
.orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序
if (StrUtil.isNotBlank(pageVO.getName())) {
taskQuery.taskNameLike("%" + pageVO.getName() + "%");
}
if (pageVO.getBeginCreateTime() != null) {
taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime());
}
if (pageVO.getEndCreateTime() != null) {
taskQuery.taskCreatedBefore(pageVO.getEndCreateTime());
}
// 执行查询
List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize());
if (CollUtil.isEmpty(tasks)) {
return PageResult.empty(taskQuery.count());
}
// 获得 TaskExtDO Map
List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
// 获得 ProcessInstance Map
Map<String, HistoricProcessInstance> historicProcessInstanceMap = processInstanceService.getHistoricProcessInstanceMap(
convertSet(tasks, HistoricTaskInstance::getProcessInstanceId));
// 获得 User Map
Map<Long, SysUserDO> userMap = userService.getUserMap(
convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())));
// 拼接结果
return new PageResult<>(BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap),
taskQuery.count());
}
@Override
public void updateTaskAssign(String id, Long userId) {
taskService.setAssignee(id, String.valueOf(userId));
}
@Override
@Transactional(rollbackFor = Exception.class)
public void approveTask(BpmTaskApproveReqVO reqVO) {
// 校验任务存在
Task task = getTask(reqVO.getId());
if (task == null) {
throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
}
// 校验流程实例存在
ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
}
// 完成任务审批通过
taskService.complete(task.getId(), instance.getProcessVariables()); // TODO 芋艿variables 的选择
// 更新任务拓展表为通过
taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()).setComment(reqVO.getComment()));
// TODO 芋艿添加评论
// taskService.addComment(task.getId(), task.getProcessInstanceId(), reqVO.getComment());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void rejectTask(@Valid BpmTaskRejectReqVO reqVO) {
// 校验任务存在
Task task = getTask(reqVO.getId());
if (task == null) {
throw exception(TASK_COMPLETE_FAIL_NOT_EXISTS);
}
// 校验流程实例存在
ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
if (instance == null) {
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
}
// 更新流程实例为不通过
processInstanceService.updateProcessInstanceResult(instance.getProcessInstanceId(),
BpmProcessInstanceResultEnum.REJECT.getResult());
// 更新任务拓展表为不通过
taskExtMapper.updateByTaskId(new BpmTaskExtDO().setTaskId(task.getId())
.setResult(BpmProcessInstanceResultEnum.REJECT.getResult()).setComment(reqVO.getComment()));
// TODO 芋艿添加评论
// taskService.addComment(task.getId(), task.getProcessInstanceId(), reqVO.getComment());
}
@Override
public TaskHandleVO getTaskSteps(TaskQueryReqVO taskQuery) {
TaskHandleVO handleVO = new TaskHandleVO();
final Task task = taskRuntime.task(taskQuery.getTaskId());
List<TaskStepVO> steps = getTaskSteps(task.getProcessInstanceId());
handleVO.setHistoryTask(steps);
return handleVO;
// TaskHandleVO handleVO = new TaskHandleVO();
// final Task task = taskRuntime.task(taskQuery.getTaskId());
// List<TaskStepVO> steps = getTaskSteps(task.getProcessInstanceId());
// handleVO.setHistoryTask(steps);
// return handleVO;
return null;
}
private List<TaskStepVO> getTaskSteps(String processInstanceId) {
// 获得已完成的活动
List<HistoricActivityInstance> finished = historyService.createHistoricActivityInstanceQuery()
@ -126,10 +237,10 @@ public class BpmTaskServiceImpl implements BpmTaskService {
// 获得对应的步骤
List<TaskStepVO> steps = new ArrayList<>();
finished.forEach(instance -> {
TaskStepVO stepVO = TaskConvert.INSTANCE.convert(instance);
TaskStepVO stepVO = BpmTaskConvert.INSTANCE.convert(instance);
stepVO.setStatus(1); // TODO @jason1 这个 magic number 要枚举起来
// TODO @jason可以考虑把 comments 读取后在统一调用 convert 拼接另外 Comment 是废弃的类有没其它可以使用的哈
List<Comment> comments = activitiTaskService.getTaskComments(instance.getTaskId());
List<Comment> comments = taskService.getTaskComments(instance.getTaskId());
if (!CollUtil.isEmpty(comments)) {
stepVO.setComment(Optional.ofNullable(comments.get(0)).map(Comment::getFullMessage).orElse(""));
}
@ -144,7 +255,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
.unfinished().list();
// 获得对应的步骤
for (HistoricActivityInstance instance : unfinished) {
TaskStepVO stepVO = TaskConvert.INSTANCE.convert(instance);
TaskStepVO stepVO = BpmTaskConvert.INSTANCE.convert(instance);
stepVO.setComment("");
stepVO.setStatus(0);
steps.add(stepVO);
@ -158,12 +269,6 @@ public class BpmTaskServiceImpl implements BpmTaskService {
return getTaskSteps(processInstanceId);
}
@Override
public TodoTaskRespVO getTaskFormKey(TaskQueryReqVO taskQuery) {
final Task task = taskRuntime.task(taskQuery.getTaskId());
return TaskConvert.INSTANCE.convert(task);
}
@Override
public FileResp getHighlightImg(String processInstanceId) {
// 查询历史
@ -172,7 +277,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
// 如果不存在实例 说明数据异常
if (hpi == null) {
throw exception(PROCESS_INSTANCE_NOT_EXISTS);
// throw exception(PROCESS_INSTANCE_NOT_EXISTS);
throw new RuntimeException("不存在");
}
// 如果有结束时间 返回model的流程图
if (!ObjectUtils.isEmpty(hpi.getEndTime())) {
@ -285,4 +391,41 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}
return highLightedFlowIds;
}
private Task getTask(String id) {
return taskService.createTaskQuery().taskId(id).singleResult();
}
// ========== Task 拓展表相关 ==========
@Override
public void createTaskExt(org.activiti.api.task.model.Task task) {
// 插入 BpmTaskExtDO 记录
BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task)
.setResult(BpmProcessInstanceResultEnum.PROCESS.getResult());
taskExtMapper.insert(taskExtDO);
}
@Override
public void updateTaskExt(org.activiti.api.task.model.Task task) {
BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task);
taskExtMapper.updateByTaskId(taskExtDO);
}
@Override
public void updateTaskExtCancel(org.activiti.api.task.model.Task task) {
BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task)
.setEndTime(new Date()) // 由于 Task 里没有办法拿到 endTime所以这里设置
.setResult(BpmProcessInstanceResultEnum.CANCEL.getResult());
taskExtMapper.updateByTaskId(taskExtDO);
}
@Override
public void updateTaskExtComplete(org.activiti.api.task.model.Task task) {
BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert(task)
.setEndTime(task.getCompletedDate())
.setResult(BpmProcessInstanceResultEnum.APPROVE.getResult());
taskExtMapper.updateByTaskId(taskExtDO);
}
}

View File

@ -0,0 +1,58 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.task.listener;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmProcessInstanceService;
import org.activiti.api.model.shared.event.RuntimeEvent;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.events.ProcessRuntimeEvent;
import org.activiti.api.process.runtime.events.listener.ProcessEventListener;
import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
import org.activiti.api.task.model.events.TaskRuntimeEvent;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 监听 {@link ProcessInstance} 的开始与完成创建与更新对应的 {@link BpmProcessInstanceExtDO} 记录
*
* @author 芋道源码
*/
@Component
public class BpmProcessInstanceEventListener<T extends RuntimeEvent<?, ?>>
implements ProcessRuntimeEventListener<T> {
@Resource
@Lazy // 解决循环依赖
private BpmProcessInstanceService processInstanceService;
@Override
@SuppressWarnings("unchecked")
public void onEvent(T rawEvent) {
// 由于 ProcessRuntimeEventListener 无法保证只监听 ProcessRuntimeEvent 事件所以通过这样的方式
if (!(rawEvent instanceof ProcessRuntimeEvent)) {
return;
}
ProcessRuntimeEvent<ProcessInstance> event = (ProcessRuntimeEvent<ProcessInstance>) rawEvent;
// 创建时插入拓展表
if (event.getEventType() == ProcessRuntimeEvent.ProcessEvents.PROCESS_CREATED) {
processInstanceService.createProcessInstanceExt(event.getEntity());
return;
}
// 取消时更新拓展表为取消
if (event.getEventType() == ProcessRuntimeEvent.ProcessEvents.PROCESS_CANCELLED) {
processInstanceService.updateProcessInstanceExtCancel(event.getEntity());
return;
}
// 完成时更新拓展表为已完成
if (event.getEventType() == ProcessRuntimeEvent.ProcessEvents.PROCESS_COMPLETED) {
processInstanceService.updateProcessInstanceExtComplete(event.getEntity());
return;
}
// 其它事件进行更新拓展表
processInstanceService.updateProcessInstanceExt(event.getEntity());
}
}

View File

@ -0,0 +1,53 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.task.listener;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
import org.activiti.api.task.runtime.events.listener.TaskEventListener;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEntityEventImpl;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 监听 {@link TaskEntity} 相关的事件设置相关属性
* 目的解决 {@link TaskEventListener} 无法解决的场景
*
* @author 芋道源码
*/
@Component
public class BpmTackActivitiEventListener implements ActivitiEventListener {
@Resource
@Lazy // 解决循环依赖
private BpmProcessDefinitionService processDefinitionService;
@Override
public void onEvent(ActivitiEvent event) {
// Task 创建时设置其分类解决 TaskService 未提供 name 的设置方法
if (ActivitiEventType.TASK_CREATED == event.getType()) {
TaskEntity task = ((TaskEntity) ((ActivitiEntityEventImpl) event).getEntity());
if (StrUtil.isNotEmpty(task.getCategory())) {
return;
}
// 设置 name
ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition2(task.getProcessDefinitionId());
if (processDefinition == null) {
return;
}
task.setCategory(processDefinition.getCategory());
}
}
@Override
public boolean isFailOnException() {
return true;
}
}

View File

@ -0,0 +1,49 @@
package cn.iocoder.yudao.adminserver.modules.bpm.service.task.listener;
import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO;
import cn.iocoder.yudao.adminserver.modules.bpm.service.task.BpmTaskService;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.events.TaskRuntimeEvent;
import org.activiti.api.task.runtime.events.listener.TaskEventListener;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* 监听 {@link Task} 的开始与完成创建与更新对应的 {@link BpmTaskExtDO} 记录
*
* @author 芋道源码
*/
@Component
public class BpmTaskEventListener<T extends TaskRuntimeEvent<? extends Task>>
implements TaskEventListener<T> {
@Resource
@Lazy // 解决循环依赖
private BpmTaskService taskService;
@Override
public void onEvent(T event) {
// 创建时插入拓展表
if (event.getEventType() == TaskRuntimeEvent.TaskEvents.TASK_CREATED) {
taskService.createTaskExt(event.getEntity());
return;
}
// 取消时更新拓展表为取消
if (event.getEventType() == TaskRuntimeEvent.TaskEvents.TASK_CANCELLED) {
taskService.updateTaskExtCancel(event.getEntity());
return;
}
// 完成时更新拓展表为已完成要注意在调用 delete ProcessInstance 才会触发该逻辑
if (event.getEventType() == TaskRuntimeEvent.TaskEvents.TASK_COMPLETED) {
taskService.updateTaskExtComplete(event.getEntity());
return;
}
// 其它事件进行更新拓展表
taskService.updateTaskExt(event.getEntity());
}
}

View File

@ -176,6 +176,9 @@ public class SysUserServiceImpl implements SysUserService {
@Override
public List<SysUserDO> getUsers(Collection<Long> ids) {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return userMapper.selectBatchIds(ids);
}

View File

@ -85,7 +85,7 @@
"@vue/compiler-sfc": "^3.0.1",
"@vue/eslint-config-prettier": "^5.0.0",
"axios": "^0.21.1",
"bpmn-js": "^7.4.0",
"bpmn-js": "^8.8.3",
"bpmn-js-properties-panel": "^0.37.2",
"camunda-bpmn-moddle": "^4.4.1",
"compression-webpack-plugin": "^6.1.1",

View File

@ -8,6 +8,14 @@ export function getProcessDefinitionPage(query) {
})
}
export function getProcessDefinitionList(query) {
return request({
url: '/bpm/process-definition/list',
method: 'get',
params: query
})
}
export function getProcessDefinitionBpmnXML(id) {
return request({
url: '/bpm/process-definition/get-bpmn-xml?id=' + id,

View File

@ -0,0 +1,28 @@
import request from '@/utils/request'
export function getMyProcessInstancePage(query) {
return request({
url: '/bpm/process-instance/my-page',
method: 'get',
params: query
})
}
export function createProcessInstance(data) {
return request({
url: '/bpm/process-instance/create',
method: 'POST',
data: data
})
}
export function cancelProcessInstance(id, reason) {
return request({
url: '/bpm/process-instance/cancel',
method: 'DELETE',
data: {
id,
reason
}
})
}

View File

@ -0,0 +1,41 @@
import request from '@/utils/request'
export function getTodoTaskPage(query) {
return request({
url: '/bpm/task/todo-page',
method: 'get',
params: query
})
}
export function getDoneTaskPage(query) {
return request({
url: '/bpm/task/done-page',
method: 'get',
params: query
})
}
export function completeTask(data) {
return request({
url: '/bpm/task/complete',
method: 'PUT',
data: data
})
}
export function approveTask(data) {
return request({
url: '/bpm/task/approve',
method: 'PUT',
data: data
})
}
export function rejectTask(data) {
return request({
url: '/bpm/task/reject',
method: 'PUT',
data: data
})
}

View File

@ -14,7 +14,6 @@
// paletteProvider: ["type", PaletteProvider]
// };
// custom/index.js
import CustomPalette from "./CustomPalette";
export default {

View File

@ -1,26 +1,36 @@
<template>
<div class="panel-tab__content">
<el-form size="mini" label-width="90px" :model="model" :rules="rules" @submit.native.prevent>
<el-form-item label="流程标识" prop="key">
<el-input v-model="model.key" placeholder="请输入流标标识"
:disabled="model.id !== undefined && model.id.length > 0" @change="handleKeyUpdate" />
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="model.name" placeholder="请输入流程名称" clearable @change="handleNameUpdate" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="model.category" placeholder="请选择流程分类" clearable style="width: 100%">
<el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="流程表单" prop="formId">
<el-select v-model="model.formId" placeholder="请选择流程表单,非必选哟!" clearable style="width: 100%">
<el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
</el-select>
</el-form-item>
<el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="model.description" clearable @change="handleDescriptionUpdate" />
</el-form-item>
<div v-if="elementBaseInfo.$type === 'bpmn:Process'"> <!-- 如果是 Process 信息的时候使用自定义表单 -->
<el-form-item label="流程标识" prop="key">
<el-input v-model="model.key" placeholder="请输入流标标识"
:disabled="model.id !== undefined && model.id.length > 0" @change="handleKeyUpdate" />
</el-form-item>
<el-form-item label="流程名称" prop="name">
<el-input v-model="model.name" placeholder="请输入流程名称" clearable @change="handleNameUpdate" />
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="model.category" placeholder="请选择流程分类" clearable style="width: 100%">
<el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="流程表单" prop="formId">
<el-select v-model="model.formId" placeholder="请选择流程表单,非必选哟!" clearable style="width: 100%">
<el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
</el-select>
</el-form-item>
<el-form-item label="流程描述" prop="description">
<el-input type="textarea" v-model="model.description" clearable @change="handleDescriptionUpdate" />
</el-form-item>
</div>
<div v-else>
<el-form-item label="ID">
<el-input v-model="elementBaseInfo.id" clearable @change="updateBaseInfo('id')"/>
</el-form-item>
<el-form-item label="名称">
<el-input v-model="elementBaseInfo.name" clearable @change="updateBaseInfo('name')" />
</el-form-item>
</div>
</el-form>
</div>
</template>

View File

@ -173,8 +173,13 @@ export default {
submitForm() {
this.$refs[this.formConf.formRef].validate(valid => {
if (!valid) return false
// sumit
this.$emit('submit', this[this.formConf.formModel])
// submit
// update by
// this.$emit('submit', this[this.formConf.formModel])
this.$emit('submit', {
conf: this.formConfCopy,
values: this[this.formConf.formModel]
})
return true
})
}

View File

@ -188,6 +188,18 @@ export const constantRoutes = [
meta: { title: '流程定义' }
}
]
}, {
path: '/bpm',
component: Layout,
hidden: true,
children: [
{
path: 'process-instance/create',
component: (resolve) => require(['@/views/bpm/processInstance/create'], resolve),
name: '发起流程',
meta: { title: '发起流程' }
}
]
},
]

View File

@ -0,0 +1,26 @@
/**
* 将毫秒转换成时间字符串例如说xx 分钟
*
* @param ms 毫秒
* @returns {string} 字符串
*/
export function getDate(ms) {
const day = Math.floor(ms / (24 * 60 * 60 * 1000));
const hour = Math.floor((ms / (60 * 60 * 1000) - day * 24));
const minute = Math.floor(((ms / (60 * 1000)) - day * 24 * 60 - hour * 60));
const second = Math.floor((ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60));
if (day > 0) {
return day + "天" + hour + "小时" + minute + "分钟";
}
if (hour > 0) {
return hour + "小时" + minute + "分钟";
}
if (minute > 0) {
return minute + "分钟";
}
if (second > 0) {
return second + "秒";
} else {
return 0 + "秒";
}
}

View File

@ -36,6 +36,8 @@ export const DICT_TYPE = {
// bpm
BPM_MODEL_CATEGORY: 'bpm_model_category',
BPM_PROCESS_INSTANCE_STATUS: 'bpm_process_instance_status',
BPM_PROCESS_INSTANCE_RESULT: 'bpm_process_instance_result',
OA_LEAVE_STATUS: 'flow_status',
OA_LEAVE_TYPE: 'oa_leave_type'
}

View File

@ -29,7 +29,7 @@
<el-tag size="medium" type="warning" v-else>未部署</el-tag>
</template>
</el-table-column>
<el-table-column label="激活状态" align="center" prop="version" width="80">
<el-table-column label="状态" align="center" prop="version" width="80">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>

View File

@ -175,17 +175,8 @@ export default {
.my-process-designer {
height: calc(100vh - 84px);
//height: 800px !important; // TODO bjs
//z-index: 0 !important;
//pointer-events: none !important;
}
.process-panel__container { // TODO
//margin-top: -800px !important;
//float: right;
//margin-left: 800px !important;
//height: 800px;
//z-index: 2147483647 !important;
//cursor:pointer !important;
.process-panel__container {
position: absolute;
right: 0;
top: 55px;

View File

@ -0,0 +1,173 @@
<template>
<div class="app-container">
<!-- 第一步通过流程定义的列表选择对应的流程 -->
<div v-if="!selectProcessInstance">
<el-table v-loading="loading" :data="list">
<el-table-column label="流程名称" align="center" prop="name" width="200">
<template slot-scope="scope">
<el-button type="text" @click="handleBpmnDetail(scope.row)">
<span>{{ scope.row.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="流程分类" align="center" prop="category" width="100">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.BPM_MODEL_CATEGORY, scope.row.category) }}</span>
</template>
</el-table-column>
<el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
<template slot-scope="scope">
<el-tag size="medium" v-if="scope.row">v{{ scope.row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="流程描述" align="center" prop="description" width="300" show-overflow-tooltip />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="text" size="small" icon="el-icon-plus" @click="handleSelect(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 第二步填写表单进行流程的提交 -->
<div v-else>
<el-card class="box-card" >
<div slot="header" class="clearfix">
<span class="el-icon-document">{{ selectProcessInstance.name }}</span>
<el-button style="float: right;" type="primary" @click="selectProcessInstance = undefined">选择其它流程</el-button>
</div>
<el-col :span="16" :offset="6">
<div>
<parser :key="new Date().getTime()" :form-conf="detailForm" @submit="submitForm" />
</div>
</el-col>
</el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span class="el-icon-picture-outline">流程图</span>
</div>
<my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
</el-card>
</div>
</div>
</template>
<script>
import {getProcessDefinitionBpmnXML, getProcessDefinitionList} from "@/api/bpm/definition";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getForm} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {createProcessInstance} from "@/api/bpm/processInstance";
export default {
name: "processDefinition",
components: {
Parser
},
data() {
return {
//
loading: true,
//
total: 0,
//
list: [],
//
detailForm: {
fields: []
},
// BPMN
bpmnXML: null,
bpmnControlForm: {
prefix: "activiti"
},
//
selectProcessInstance: undefined, //
//
categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
};
},
created() {
this.getList();
},
methods: {
/** 查询流程定义列表 */
getList() {
this.loading = true;
getProcessDefinitionList({
suspensionState: 1
}).then(response => {
this.list = response.data
this.loading = false
}
);
},
/** 处理选择流程的按钮操作 **/
handleSelect(row) {
//
if (!row.formId) {
this.$message.error('该流程未绑定表单,无法发起流程!请重新选择你要发起的流程');
return;
}
//
this.selectProcessInstance = row;
//
getForm(row.formId).then(response => {
//
const data = response.data
this.detailForm = {
...JSON.parse(data.conf),
fields: decodeFields(data.fields)
}
});
//
getProcessDefinitionBpmnXML(row.id).then(response => {
this.bpmnXML = response.data
})
},
/** 提交按钮 */
submitForm(params) {
if (!params) {
return;
}
//
const conf = params.conf;
conf.disabled = true; //
conf.formBtns = false; //
//
const variables = params.values;
createProcessInstance({
processDefinitionId: this.selectProcessInstance.id,
variables: variables
}).then(response => {
this.msgSuccess("发起流程成功");
//
this.$store.dispatch("tagsView/delView", this.$route);
this.$router.go(-1);
}).catch(() => {
conf.disabled = false; //
conf.formBtns = true; //
})
},
}
};
</script>
<style lang="scss">
.my-process-designer {
height: calc(100vh - 200px);
}
.box-card {
width: 100%;
margin-bottom: 20px;
}
</style>

View File

@ -0,0 +1,209 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="所属流程" prop="processDefinitionId">
<el-input v-model="queryParams.processDefinitionId" placeholder="请输入流程定义的编号" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="流程分类" prop="category">
<el-select v-model="queryParams.category" placeholder="请选择流程分类" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="提交时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="结果" prop="result">
<el-select v-model="queryParams.result" placeholder="请选择流结果" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">发起流程</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" width="320" />
<el-table-column label="流程名" align="center" prop="name" />
<el-table-column label="流程分类" align="center" prop="category">
<template slot-scope="scope">
<span>{{ getDictDataLabel(DICT_TYPE.BPM_MODEL_CATEGORY, scope.row.category) }}</span>
</template>
</el-table-column>
<el-table-column label="当前审批任务" align="center" prop="tasks">
<template slot-scope="scope">
<el-button v-for="task in scope.row.tasks" type="text" @click="handleFormDetail(task.id)">
<span>{{ task.name }}</span>
</el-button>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<span>
<el-tag type="primary" v-if="scope.row.status === 1"> <!-- 进行中 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, scope.row.status) }}
</el-tag>
<el-tag type="success" v-if="scope.row.status === 2"> <!-- 已结束 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, scope.row.status) }}
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="结果" align="center" prop="result">
<template slot-scope="scope">
<span>
<el-tag type="primary" v-if="scope.row.result === 1"> <!-- 进行中 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="success" v-if="scope.row.result === 2"> <!-- 通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="danger" v-if="scope.row.result === 3"> <!-- 不通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="info" v-if="scope.row.result === 4"> <!-- 撤回 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="提交时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- TODO 芋艿权限 -->
<el-button type="text" size="small" icon="el-icon-delete" v-if="scope.row.result === 1"
@click="handleCancel(scope.row)">取消</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {
getMyProcessInstancePage,
createProcessInstanceExt,
updateProcessInstanceExt,
deleteProcessInstanceExt,
getProcessInstanceExt,
getProcessInstanceExtPage,
exportProcessInstanceExtExcel, cancelProcessInstance
} from "@/api/bpm/processInstance";
import {deleteErrorCode} from "@/api/system/errorCode";
export default {
name: "ProcessInstanceExt",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
dateRangeCreateTime: [],
//
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
processDefinitionId: null,
category: null,
status: null,
result: null,
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
//
getMyProcessInstancePage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 **/
handleAdd() {
this.$router.push({ path: "/bpm/process-instance/create"})
},
/** 取消按钮操作 */
handleCancel(row) {
const id = row.id;
this.$prompt('请输出取消原因?', "取消流程", {
type: 'warning',
confirmButtonText: "确定",
cancelButtonText: "取消",
inputPattern: /^[\s\S]*.*[^\s][\s\S]*$/, //
inputErrorMessage: "取消原因不能为空",
}).then(({ value }) => {
return cancelProcessInstance(id, value);
}).then(() => {
this.getList();
this.msgSuccess("取消成功");
})
},
}
};
</script>

View File

@ -0,0 +1,133 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务编号" align="center" prop="id" width="320" fixed />
<el-table-column label="任务名称" align="center" prop="name" width="200" />
<el-table-column label="所属流程" align="center" prop="processInstance.name" width="200" />
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" />
<el-table-column label="结果" align="center" prop="result">
<template slot-scope="scope">
<span>
<el-tag type="primary" v-if="scope.row.result === 1"> <!-- 进行中 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="success" v-if="scope.row.result === 2"> <!-- 通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="danger" v-if="scope.row.result === 3"> <!-- 不通过 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
<el-tag type="info" v-if="scope.row.result === 4"> <!-- 撤回 -->
{{ getDictDataLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="审批意见" align="center" prop="comment" width="200" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="审批时间" align="center" prop="endTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="耗时" align="center" prop="durationInMillis" width="180">
<template slot-scope="scope">
<span>{{ getDateStar(scope.row.durationInMillis) }}</span>
</template>
</el-table-column>
<!-- TODO 耗时 -->
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- TODO 权限颜色 -->
<el-button size="mini" type="text" icon="el-icon-edit">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {getDoneTaskPage} from '@/api/bpm/task'
import {getDate} from "@/utils/dateUtils";
export default {
name: "Done",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
dateRangeCreateTime: [],
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
getDoneTaskPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
getDateStar(ms) {
return getDate(ms);
}
}
};
</script>

View File

@ -0,0 +1,122 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="流程名" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入流程名" clearable size="small" @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间">
<el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务编号" align="center" prop="id" width="320" />
<el-table-column label="任务名称" align="center" prop="name" />
<el-table-column label="所属流程" align="center" prop="processInstance.name" />
<el-table-column label="流程发起人" align="center" prop="processInstance.startUserNickname" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="version" width="80">
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
<el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- TODO 权限颜色 -->
<el-button size="mini" type="text" icon="el-icon-edit">审批</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="audit(scope.row, true)">通过</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="audit(scope.row, false)">不通过</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.suspensionState === 2">激活</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.suspensionState === 1">挂起</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {approveTask, getTodoTaskPage, rejectTask} from '@/api/bpm/task'
export default {
name: "Todo",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
dateRangeCreateTime: [],
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
let params = {...this.queryParams};
this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
getTodoTaskPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
audit(row, pass) {
if (pass) {
approveTask({
id: row.id,
comment: '通过'
})
} else {
rejectTask({
id: row.id,
comment: '不通过'
})
}
}
}
};
</script>

View File

@ -1,286 +0,0 @@
<template>
<div class="app-container">
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态">
<el-option
v-for="dict in leaveStatusData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="任务Id" align="center" prop="id" />
<el-table-column label="流程名称" align="center" prop="processName" />
<el-table-column label="任务状态" align="center" :formatter="statusFormat" prop="status" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.status == 1" @click="handleClaim(scope.row)">签收</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" v-if="scope.row.status == 2" @click="getTaskFormKey(scope.row)">办理</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
<el-tabs tab-position="left" style="height: 500px;">
<el-tab-pane label="详情">
<el-form ref="form" :model="handleTask.formObject" label-width="80px">
<el-form-item label="状态" >
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_STATUS, handleTask.formObject.status) }}
</el-form-item>
<el-form-item label="申请人id" >{{handleTask.formObject.userId}}</el-form-item>
<el-form-item label="开始时间" >{{ parseTime(handleTask.formObject.startTime) }}</el-form-item>
<el-form-item label="结束时间" prop="endTime">{{ parseTime(handleTask.formObject.endTime) }}</el-form-item>
<el-form-item label="请假类型" prop="leaveType">
{{ getDictDataLabel(DICT_TYPE.OA_LEAVE_TYPE, handleTask.formObject.leaveType) }}
</el-form-item>
<el-form-item label="原因" prop="reason">{{handleTask.formObject.reason}}</el-form-item>
<el-form-item label="申请时间" prop="applyTime">{{ parseTime(handleTask.formObject.applyTime) }}</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="任务处理">
<el-steps :active="handleTask.historyTask.length-1" simple finish-status="success">
<el-step :title="item.stepName" icon="el-icon-edit" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-steps direction="vertical" :active="handleTask.historyTask.length-1" finish-status="success" space="60px">
<el-step :title="item.stepName" :description="item.comment" v-for="(item) in handleTask.historyTask" ></el-step>
</el-steps>
<br/>
<el-form ref="taskForm" :model="task" label-width="80px" v-show="handleTask.taskVariable !=''">
<el-form-item label="处理意见" prop="approved">
<el-select v-model="task.approved" placeholder="处理意见">
<el-option
v-for="dict in approvedData"
:key="parseInt(dict.value)"
:label="dict.label"
:value="parseInt(dict.value)"
/>
</el-select>
</el-form-item>
<el-input
type="textarea"
:rows="2"
v-model="task.comment">
</el-input>
</el-form>
<br/>
<el-button type="primary" @click="submitTask">提交</el-button>
</el-tab-pane>
</el-tabs>
</el-dialog>
</div>
</template>
<script>
import { completeTask, taskSteps, getTaskFormKey,deleteLeave, getLeave, getTodoTaskPage, claimTask } from "@/api/oa/todo";
import { getDictDataLabel, getDictDatas, DICT_TYPE } from '@/utils/dict'
export default {
name: "Todo",
components: {
},
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10
},
//
form: {},
handleTask: {
historyTask:[{
stepName:"步骤一"
}
],
taskVariable: "",
formObject: {}
},
steps:[{
stepName:"步骤一"
}],
task: {
approved : 1,
variables: {},
taskId: undefined,
comment: ""
},
rules: {
},
leaveTypeDictData: getDictDatas(DICT_TYPE.OA_LEAVE_TYPE),
leaveStatusData: getDictDatas(DICT_TYPE.OA_LEAVE_STATUS),
approvedData: [
{
value: 1,
label: '同意'
},
{
value: 0,
label: '不同意'
}
]
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
let params = {...this.queryParams};
//
getTodoTaskPage(params).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
processInstanceId: undefined,
status: undefined,
userId: undefined,
startTime: undefined,
endTime: undefined,
leaveType: undefined,
reason: undefined,
applyTime: undefined,
};
this.resetForm("form");
},
statusFormat(row, column) {
return row.status == 1 ? "未签收" : "已签收";
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeStartTime = [];
this.dateRangeEndTime = [];
this.dateRangeApplyTime = [];
this.resetForm("queryForm");
this.handleQuery();
},
getTaskFormKey(row) {
const taskId = row.id;
const data = {
taskId : taskId
}
getTaskFormKey(data).then(response => {
const resp = response.data;
const path = resp.formKey;
const taskId = resp.id;
const businessKey = resp.businessKey;
const route = {
path: path,
query: {
businessKey: businessKey,
taskId:taskId,
processInstanceId : resp.processInstanceId
}
}
this.$router.replace(route);
});
},
handleLeaveApprove(row) {
this.reset();
const businessKey = row.businessKey;
const taskId = row.id;
const processKey = row.processKey;
const data = {
taskId : taskId,
businessKey: businessKey,
processKey: processKey
}
taskSteps(data).then(response => {
this.form = {};
this.handleTask = response.data;
this.task.taskId = taskId;
this.open = true;
this.title = "任务办理";
});
},
/** 任务签收操作 */
handleClaim(row) {
this.reset();
const id = row.id;
claimTask(id).then(() => {
this.getList();
this.msgSuccess("签收成功");
});
},
/** 提交任务 */
submitTask() {
const taskVariableName = this.handleTask.taskVariable;
if (taskVariableName != "") {
if (this.task.approved == 1) {
this.task.variables[taskVariableName] = true;
}
if (this.task.approved == 0) {
this.task.variables[taskVariableName] = false;
}
}
completeTask(this.task).then(response => {
this.msgSuccess("执行任务成功");
this.open = false;
this.getList();
})
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$confirm('是否确认删除请假申请编号为"' + id + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return deleteLeave(id);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
})
}
}
};
</script>

View File

@ -25,7 +25,9 @@ public interface WebFilterOrderEnum {
// Spring Security Filter 默认为 -100可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类
int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后
int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面
int ACTIVITI_FILTER = -98; // 需要保证在 Spring Security 过滤后面
int DEMO_FILTER = Integer.MAX_VALUE;

View File

@ -35,4 +35,8 @@ public final class PageResult<T> implements Serializable {
return new PageResult<>(0L);
}
public static <T> PageResult<T> empty(Long total) {
return new PageResult<>(total);
}
}

View File

@ -37,6 +37,14 @@
<artifactId>yudao-common</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
@ -72,6 +80,7 @@
<artifactId>activiti-image-generator</artifactId>
<version>${activiti.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,9 +1,12 @@
package cn.iocoder.yudao.framework.activiti.config;
import cn.iocoder.yudao.framework.activiti.core.web.ActivitiWebFilter;
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -26,4 +29,12 @@ public class YudaoActivitiConfiguration {
return springProcessEngineConfiguration -> springProcessEngineConfiguration.setSqlSessionFactory(sqlSessionFactory);
}
@Bean
public FilterRegistrationBean<ActivitiWebFilter> activitiWebFilter() {
FilterRegistrationBean<ActivitiWebFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new ActivitiWebFilter());
registrationBean.setOrder(WebFilterOrderEnum.ACTIVITI_FILTER);
return registrationBean;
}
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.framework.activiti.core.util;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.activiti.engine.impl.persistence.entity.HistoricScopeInstanceEntityImpl;
/**
* Activiti 工具类
*
* @author 芋道源码
*/
public class ActivitiUtils {
static {
setAuthenticationThreadLocal();
}
// ========== Authentication 相关 ==========
/**
* 反射修改 Authentication authenticatedUserIdThreadLocal 静态变量使用 TTL 线程变量
* 目的保证 @Async 等异步执行时变量丢失的问题
*/
private static void setAuthenticationThreadLocal() {
ReflectUtil.setFieldValue(Authentication.class, "authenticatedUserIdThreadLocal",
new TransmittableThreadLocal<String>());
}
public static void setAuthenticatedUserId(Long userId) {
Authentication.setAuthenticatedUserId(String.valueOf(userId));
}
public static void clearAuthenticatedUserId() {
Authentication.setAuthenticatedUserId(null);
}
}

View File

@ -0,0 +1,37 @@
package cn.iocoder.yudao.framework.activiti.core.web;
import cn.iocoder.yudao.framework.activiti.core.util.ActivitiUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Activiti Web 过滤器 userId 设置到 {@link org.activiti.engine.impl.identity.Authentication}
*
* @author 芋道源码
*/
public class ActivitiWebFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
try {
// 设置工作流的用户
Long userId = SecurityFrameworkUtils.getLoginUserId();
if (userId != null) {
ActivitiUtils.setAuthenticatedUserId(userId);
}
// 过滤
chain.doFilter(request, response);
} finally {
// 清理
ActivitiUtils.clearAuthenticatedUserId();
}
}
}

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.mybatis.core.dataobject;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;

View File

@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
import java.util.List;
/**
@ -44,4 +45,8 @@ public interface BaseMapperX<T> extends BaseMapper<T> {
return selectList(new QueryWrapper<T>().eq(field, value));
}
default List<T> selectList(String field, Collection<?> values) {
return selectList(new QueryWrapper<T>().in(field, values));
}
}

View File

@ -98,15 +98,6 @@ public class SecurityFrameworkUtils {
// 原因是Spring Security Filter ApiAccessLogFilter 后面在它记录访问日志时线上上下文已经没有用户编号等信息
WebFrameworkUtils.setLoginUserId(request, loginUser.getId());
WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType());
// // TODO @jason使用 userId 会不会更合适哈
// // TODO @芋艿activiti 需要使用 ttl 上下文
// // TODO @jason清理问题
// if (Objects.equals(UserTypeEnum.ADMIN.getValue(), loginUser.getUserType())) {
// org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
// }
// // TODO @芋道源码 该值被赋值给 user task 中assignee username 显示更直白一点
// // TODO @jason有办法设置 userId然后 activiti 有地方读取到 username 毕竟 username 只是 User 的登陆账号并不能绝对像 userId 代表一个用户
// org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(loginUser.getUsername());
}
}

View File

@ -27,7 +27,11 @@
### ⭐ New Features
* 【优化】引入 form generator 0.2.0 版本,并重构相关代码
* 【新增】新增流程表单,支持动态进行表单的配置
* 【新增】流程表单,支持动态进行表单的配置
* 【新增】流程模型的管理,支持新增、导入、编辑、删除、发布流程模型
* 【新增】我的流程的管理,支持发起流程
* 【新增】待办任务的管理,支持任务的审批通过与不通过
* 【新增】已办任务的管理,支持详情的查看
* 【新增】引入 bpmn-process-designer 0.0.1 版本,提供流程设计器的能力
### 🐞 Bug Fixes