diff --git a/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/src/main/java/com/ruoyi/framework/config/SecurityConfig.java index bdb7199..75eac5c 100644 --- a/src/main/java/com/ruoyi/framework/config/SecurityConfig.java +++ b/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -111,7 +111,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter // 过滤请求 .authorizeRequests() // 对于登录login 注册register 验证码captchaImage 允许匿名访问 - .antMatchers("/login", "/register", "/captchaImage").permitAll() + .antMatchers("/login", "/register", "/captchaImage","/getLoginCode").permitAll() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() diff --git a/src/main/java/com/ruoyi/project/common/CaptchaController.java b/src/main/java/com/ruoyi/project/common/CaptchaController.java index 13dd70f..9ca494e 100644 --- a/src/main/java/com/ruoyi/project/common/CaptchaController.java +++ b/src/main/java/com/ruoyi/project/common/CaptchaController.java @@ -2,10 +2,13 @@ package com.ruoyi.project.common; import java.awt.image.BufferedImage; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletResponse; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.FastByteArrayOutputStream; @@ -22,12 +25,11 @@ import com.ruoyi.project.system.service.ISysConfigService; /** * 验证码操作处理 - * + * * @author ruoyi */ @RestController -public class CaptchaController -{ +public class CaptchaController { @Resource(name = "captchaProducer") private Producer captchaProducer; @@ -36,11 +38,11 @@ public class CaptchaController @Autowired private RedisCache redisCache; - + // 验证码类型 @Value("${ruoyi.captchaType}") private String captchaType; - + @Autowired private ISysConfigService configService; @@ -48,13 +50,11 @@ public class CaptchaController * 生成验证码 */ @GetMapping("/captchaImage") - public AjaxResult getCode(HttpServletResponse response) throws IOException - { + public AjaxResult getCode(HttpServletResponse response) throws IOException { AjaxResult ajax = AjaxResult.success(); boolean captchaEnabled = configService.selectCaptchaEnabled(); ajax.put("captchaEnabled", captchaEnabled); - if (!captchaEnabled) - { + if (!captchaEnabled) { return ajax; } @@ -66,15 +66,12 @@ public class CaptchaController BufferedImage image = null; // 生成验证码 - if ("math".equals(captchaType)) - { + if ("math".equals(captchaType)) { String capText = captchaProducerMath.createText(); capStr = capText.substring(0, capText.lastIndexOf("@")); code = capText.substring(capText.lastIndexOf("@") + 1); image = captchaProducerMath.createImage(capStr); - } - else if ("char".equals(captchaType)) - { + } else if ("char".equals(captchaType)) { capStr = code = captchaProducer.createText(); image = captchaProducer.createImage(capStr); } @@ -82,12 +79,9 @@ public class CaptchaController redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); // 转换流信息写出 FastByteArrayOutputStream os = new FastByteArrayOutputStream(); - try - { + try { ImageIO.write(image, "jpg", os); - } - catch (IOException e) - { + } catch (IOException e) { return AjaxResult.error(e.getMessage()); } @@ -95,4 +89,19 @@ public class CaptchaController ajax.put("img", Base64.encode(os.toByteArray())); return ajax; } + + + @GetMapping("/getLoginCode") + public AjaxResult getLoginCode(HttpServletResponse response) throws IOException { + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + String verification = IdUtils.simpleUUID(); + redisCache.setCacheObject(verifyKey, verification, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + Map map = new HashMap<>(); + map.put("uuid", uuid); + map.put("verification", verification); + + return AjaxResult.success(map); + } } diff --git a/src/main/java/com/ruoyi/project/flowable/controller/FlowController.java b/src/main/java/com/ruoyi/project/flowable/controller/FlowController.java index e5910a6..e07d281 100644 --- a/src/main/java/com/ruoyi/project/flowable/controller/FlowController.java +++ b/src/main/java/com/ruoyi/project/flowable/controller/FlowController.java @@ -66,7 +66,7 @@ public class FlowController extends BaseController { * @return 数据 */ @ApiOperation(value = "读取xml文件") - @GetMapping("/readXml/{deployId}") + @PostMapping("/readXml/{deployId}") public AjaxResult readXml(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId) { try { return flowUtils.readXml(deployId); @@ -83,7 +83,7 @@ public class FlowController extends BaseController { * @return 数据 */ @ApiOperation(value = "激活或挂起流程定义") - @PutMapping(value = "/updateState") + @PostMapping(value = "/updateState") public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, @ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) { flowUtils.updateState(state, deployId); @@ -97,7 +97,7 @@ public class FlowController extends BaseController { * @return 数据 */ @ApiOperation(value = "删除流程") - @DeleteMapping(value = "/{deployIds}") + @PostMapping(value = "/{deployIds}") public AjaxResult delete(@ApiParam(value = "流程定义id数组", required = true) @PathVariable String[] deployIds) { if (deployIds == null || deployIds.length == 0) { return AjaxResult.error("删除的流程定义id不能为空"); diff --git a/src/main/java/com/ruoyi/project/flowable/controller/FlowTaskController.java b/src/main/java/com/ruoyi/project/flowable/controller/FlowTaskController.java index 25de793..2970dc3 100644 --- a/src/main/java/com/ruoyi/project/flowable/controller/FlowTaskController.java +++ b/src/main/java/com/ruoyi/project/flowable/controller/FlowTaskController.java @@ -1,8 +1,13 @@ package com.ruoyi.project.flowable.controller; import com.ruoyi.framework.web.controller.BaseController; +import com.ruoyi.framework.web.domain.AjaxResult; +import com.ruoyi.project.flowable.domain.RequestEntity; import com.ruoyi.project.flowable.utils.FlowTaskUtils; +import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; +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; @@ -15,11 +20,90 @@ import javax.annotation.Resource; @RestController @RequestMapping("/flowTask") public class FlowTaskController extends BaseController { + @Resource private FlowTaskUtils flowTaskUtils; + @ApiOperation(value = "获取单个流程详情") + @PostMapping(value = "/selectSingleFlow") + public AjaxResult selectSingleFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.selectSingleFlow(entity.getUserId(), entity.getProInsId()); + } + + @ApiOperation(value = "获取多个流程详情") + @PostMapping(value = "/selectAggregateFlow") + public AjaxResult selectAggregateFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.selectAggregateFlow(entity.getUserId(), entity.getProDefKey(), entity.getPageSize(), entity.getPageNum()); + } + + @ApiOperation(value = "发起流程") + @PostMapping(value = "/startFlow") + public AjaxResult startFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.startFlow(entity.getUserId(), entity.getProDefKey(), entity.getProcessVariables()); + } + + @ApiOperation(value = "获取待办流程") + @PostMapping(value = "/getStayAllFlow") + public AjaxResult getStayAllFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.getStayAllFlow(entity.getUserId(), entity.getPageSize(), entity.getPageNum()); + } + + @ApiOperation(value = "获取待办流程") + @PostMapping(value = "/getStayFlow") + public AjaxResult getStayFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.getStayFlow(entity.getUserId(), entity.getProDefKey(), entity.getPageSize(), entity.getPageNum()); + } + + @ApiOperation(value = "获取所有已办流程") + @PostMapping(value = "/getCompletedAllFlow") + public AjaxResult getCompletedAllFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.getCompletedAllFlow(entity.getUserId(), entity.getPageSize(), entity.getPageNum()); + } + + @ApiOperation(value = "获取已办流程") + @PostMapping(value = "/getCompletedFlow") + public AjaxResult getCompletedFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.getCompletedFlow(entity.getUserId(), entity.getProDefKey(), entity.getPageSize(), entity.getPageNum()); + } + @ApiOperation(value = "流程历史流转记录") + @PostMapping(value = "/recordFlow") + public AjaxResult recordFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.recordFlow(entity.getProInsId()); + } + + @ApiOperation(value = "取消申请") + @PostMapping(value = "/stopFlow") + public AjaxResult stopFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.stopFlow(entity.getProInsId()); + } + + @ApiOperation(value = "撤回流程") + @PostMapping(value = "/revokeFlow") + public AjaxResult revokeFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.revokeFlow(entity.getProInsId()); + } + + + @ApiOperation(value = "流程审批") + @PostMapping(value = "/approvalFlow") + public AjaxResult approvalFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.approvalFlow(entity.getUserId(), entity.getTaskId(), entity.getProInsId(), entity.getOptions(), entity.getProcessVariables()); + } + + @ApiOperation(value = "驳回任务") + @PostMapping(value = "/rejectFlow") + public AjaxResult rejectFlow(@RequestBody RequestEntity entity) { + return flowTaskUtils.rejectFlow(entity.getTaskId(), entity.getTargetKey(), entity.getOptions()); + } + + + @ApiOperation(value = "获取流程变量") + @PostMapping(value = "/processVariables") + public AjaxResult processVariables(@RequestBody RequestEntity entity) { + return flowTaskUtils.processVariables(entity.getTaskId()); + } } diff --git a/src/main/java/com/ruoyi/project/flowable/domain/FlowCommentDto.java b/src/main/java/com/ruoyi/project/flowable/domain/FlowCommentDto.java new file mode 100644 index 0000000..6c0491b --- /dev/null +++ b/src/main/java/com/ruoyi/project/flowable/domain/FlowCommentDto.java @@ -0,0 +1,25 @@ +package com.ruoyi.project.flowable.domain; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Tony + * @date 2021/3/28 15:50 + */ +@Data +@Builder +public class FlowCommentDto implements Serializable { + + /** + * 意见类别 0 正常意见 1 退回意见 2 驳回意见 + */ + private String type; + + /** + * 意见内容 + */ + private String comment; +} diff --git a/src/main/java/com/ruoyi/project/flowable/domain/FlowTaskEntity.java b/src/main/java/com/ruoyi/project/flowable/domain/FlowTaskEntity.java index 41683da..1b066fd 100644 --- a/src/main/java/com/ruoyi/project/flowable/domain/FlowTaskEntity.java +++ b/src/main/java/com/ruoyi/project/flowable/domain/FlowTaskEntity.java @@ -8,6 +8,7 @@ import lombok.Setter; import java.io.Serializable; import java.util.Date; +import java.util.Map; /** * 工作流实例 @@ -97,4 +98,14 @@ public class FlowTaskEntity implements Serializable { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date finishTime; + @ApiModelProperty("流转参数") + private Map variables; + + + @ApiModelProperty("任务意见") + private FlowCommentDto comment; + + @ApiModelProperty("审批意见") + private String opinions; + } diff --git a/src/main/java/com/ruoyi/project/flowable/domain/RequestEntity.java b/src/main/java/com/ruoyi/project/flowable/domain/RequestEntity.java new file mode 100644 index 0000000..0ee2309 --- /dev/null +++ b/src/main/java/com/ruoyi/project/flowable/domain/RequestEntity.java @@ -0,0 +1,61 @@ +package com.ruoyi.project.flowable.domain; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.models.auth.In; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author bonus + * 请求参数实体类 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestEntity implements Serializable { + + /** + * 用户id + */ + private String userId; + /** + * 流程定义id + */ + private String proDefId; + /** + * 流程定义标识 + */ + private String proDefKey; + /** + * 流程实例id + */ + private String proInsId; + /** + * 流程部署编号 + */ + private String deployId; + /** + * 流程携带参数 + */ + private Map processVariables; + /** + * 当前页码 + */ + private Integer pageSize; + /** + * 每页条数 + */ + private Integer pageNum; + /** + * 审批意见 + */ + private String options; + + private String taskId; + + private String targetKey; +} diff --git a/src/main/java/com/ruoyi/project/flowable/domain/TaskEntity.java b/src/main/java/com/ruoyi/project/flowable/domain/TaskEntity.java new file mode 100644 index 0000000..8024f03 --- /dev/null +++ b/src/main/java/com/ruoyi/project/flowable/domain/TaskEntity.java @@ -0,0 +1,75 @@ +package com.ruoyi.project.flowable.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * @author bonus + * 流程定义相关的数据传输对象(DTO),封装了流程实例的各种属性及内部变量信息,便于序列化与反序列化处理。 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TaskEntity implements Serializable { + /** + * 任务所在的执行流ID + */ + private String executionId; + /** + * 对应的流程实例ID + */ + private String proInstId; + /** + * 对应流程定义数据的ID + */ + private String proDefId; + /** + * 父任务 + */ + private String parentTaskId; + /** + * 任务定义的ID值 + */ + private String proDefKey; + /** + * 任务名称 + */ + private String name; + /** + * 任务描述 + */ + private String description; + /** + * 拥有人,没有做外键关联 + */ + private String owner; + /** + * 代理人,没有做外键关联 + */ + private String assignee; + /** + * 委托状态:PENDING 委托中,RESOLVED 已处理 + */ + private DelegationStatus delegationStatus; + /** + * 优先级 + */ + private Integer priority; + /** + * 过期时间 + */ + private String dueDate; + /** + * 创建时间 + */ + private String createTime; + + + public enum DelegationStatus { + PENDING, RESOLVED + } + +} diff --git a/src/main/java/com/ruoyi/project/flowable/domain/test.java b/src/main/java/com/ruoyi/project/flowable/domain/test.java new file mode 100644 index 0000000..23b0554 --- /dev/null +++ b/src/main/java/com/ruoyi/project/flowable/domain/test.java @@ -0,0 +1,389 @@ +package com.ruoyi.project.flowable.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@Data +public class test { + @JsonProperty("id") + private String id; + @JsonProperty("revision") + private Integer revision; + @JsonProperty("originalPersistentState") + private OriginalPersistentStateDTO originalPersistentState; + @JsonProperty("variableInstances") + private VariableInstancesDTO variableInstances; + @JsonProperty("usedVariablesCache") + private UsedVariablesCacheDTO usedVariablesCache; + @JsonProperty("transientVariables") + private TransientVariablesDTO transientVariables; + @JsonProperty("cachedElContext") + private Object cachedElContext; + + @NoArgsConstructor + @Data + public static class OriginalPersistentStateDTO { + } + + @NoArgsConstructor + @Data + public static class VariableInstancesDTO { + @JsonProperty("startUserId") + private StartUserIdDTO startUserId; + @JsonProperty("procDefKey") + private ProcDefKeyDTO procDefKey; + @JsonProperty("INITIATOR") + private INITIATORDTO initiator; + + @NoArgsConstructor + @Data + public static class StartUserIdDTO { + @JsonProperty("id") + private String id; + @JsonProperty("revision") + private Integer revision; + @JsonProperty("originalPersistentState") + private OriginalPersistentStateDTO originalPersistentState; + @JsonProperty("name") + private String name; + @JsonProperty("type") + private TypeDTO type; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("executionId") + private String executionId; + @JsonProperty("processInstanceId") + private String processInstanceId; + @JsonProperty("processDefinitionId") + private String processDefinitionId; + @JsonProperty("taskId") + private Object taskId; + @JsonProperty("scopeId") + private Object scopeId; + @JsonProperty("subScopeId") + private Object subScopeId; + @JsonProperty("scopeType") + private Object scopeType; + @JsonProperty("longValue") + private Object longValue; + @JsonProperty("doubleValue") + private Object doubleValue; + @JsonProperty("textValue") + private String textValue; + @JsonProperty("textValue2") + private Object textValue2; + @JsonProperty("byteArrayRef") + private ByteArrayRefDTO byteArrayRef; + @JsonProperty("cachedValue") + private String cachedValue; + @JsonProperty("deleted") + private Boolean deleted; + @JsonProperty("bytes") + private Object bytes; + @JsonProperty("persistentState") + private PersistentStateDTO persistentState; + @JsonProperty("value") + private String value; + @JsonProperty("readOnly") + private Boolean readOnly; + @JsonProperty("updated") + private Boolean updated; + @JsonProperty("inserted") + private Boolean inserted; + @JsonProperty("idPrefix") + private String idPrefix; + @JsonProperty("revisionNext") + private Integer revisionNext; + + @NoArgsConstructor + @Data + public static class OriginalPersistentStateDTO { + } + + @NoArgsConstructor + @Data + public static class TypeDTO { + @JsonProperty("cachable") + private Boolean cachable; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("readOnly") + private Boolean readOnly; + } + + @NoArgsConstructor + @Data + public static class ByteArrayRefDTO { + @JsonProperty("id") + private Object id; + @JsonProperty("name") + private Object name; + @JsonProperty("deleted") + private Boolean deleted; + } + + @NoArgsConstructor + @Data + public static class PersistentStateDTO { + @JsonProperty("executionId") + private String executionId; + @JsonProperty("scopeId") + private Object scopeId; + @JsonProperty("subScopeId") + private Object subScopeId; + @JsonProperty("textValue") + private String textValue; + @JsonProperty("scopeType") + private Object scopeType; + @JsonProperty("name") + private String name; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("doubleValue") + private Object doubleValue; + @JsonProperty("longValue") + private Object longValue; + @JsonProperty("textValue2") + private Object textValue2; + } + } + + @NoArgsConstructor + @Data + public static class ProcDefKeyDTO { + @JsonProperty("id") + private String id; + @JsonProperty("revision") + private Integer revision; + @JsonProperty("originalPersistentState") + private OriginalPersistentStateDTO originalPersistentState; + @JsonProperty("name") + private String name; + @JsonProperty("type") + private TypeDTO type; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("executionId") + private String executionId; + @JsonProperty("processInstanceId") + private String processInstanceId; + @JsonProperty("processDefinitionId") + private String processDefinitionId; + @JsonProperty("taskId") + private Object taskId; + @JsonProperty("scopeId") + private Object scopeId; + @JsonProperty("subScopeId") + private Object subScopeId; + @JsonProperty("scopeType") + private Object scopeType; + @JsonProperty("longValue") + private Object longValue; + @JsonProperty("doubleValue") + private Object doubleValue; + @JsonProperty("textValue") + private String textValue; + @JsonProperty("textValue2") + private Object textValue2; + @JsonProperty("byteArrayRef") + private ByteArrayRefDTO byteArrayRef; + @JsonProperty("cachedValue") + private String cachedValue; + @JsonProperty("deleted") + private Boolean deleted; + @JsonProperty("bytes") + private Object bytes; + @JsonProperty("persistentState") + private PersistentStateDTO persistentState; + @JsonProperty("value") + private String value; + @JsonProperty("readOnly") + private Boolean readOnly; + @JsonProperty("updated") + private Boolean updated; + @JsonProperty("inserted") + private Boolean inserted; + @JsonProperty("idPrefix") + private String idPrefix; + @JsonProperty("revisionNext") + private Integer revisionNext; + + @NoArgsConstructor + @Data + public static class OriginalPersistentStateDTO { + } + + @NoArgsConstructor + @Data + public static class TypeDTO { + @JsonProperty("cachable") + private Boolean cachable; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("readOnly") + private Boolean readOnly; + } + + @NoArgsConstructor + @Data + public static class ByteArrayRefDTO { + @JsonProperty("id") + private Object id; + @JsonProperty("name") + private Object name; + @JsonProperty("deleted") + private Boolean deleted; + } + + @NoArgsConstructor + @Data + public static class PersistentStateDTO { + @JsonProperty("executionId") + private String executionId; + @JsonProperty("scopeId") + private Object scopeId; + @JsonProperty("subScopeId") + private Object subScopeId; + @JsonProperty("textValue") + private String textValue; + @JsonProperty("scopeType") + private Object scopeType; + @JsonProperty("name") + private String name; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("doubleValue") + private Object doubleValue; + @JsonProperty("longValue") + private Object longValue; + @JsonProperty("textValue2") + private Object textValue2; + } + } + + @NoArgsConstructor + @Data + public static class INITIATORDTO { + @JsonProperty("id") + private String id; + @JsonProperty("revision") + private Integer revision; + @JsonProperty("originalPersistentState") + private OriginalPersistentStateDTO originalPersistentState; + @JsonProperty("name") + private String name; + @JsonProperty("type") + private TypeDTO type; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("executionId") + private String executionId; + @JsonProperty("processInstanceId") + private String processInstanceId; + @JsonProperty("processDefinitionId") + private String processDefinitionId; + @JsonProperty("taskId") + private Object taskId; + @JsonProperty("scopeId") + private Object scopeId; + @JsonProperty("subScopeId") + private Object subScopeId; + @JsonProperty("scopeType") + private Object scopeType; + @JsonProperty("longValue") + private Object longValue; + @JsonProperty("doubleValue") + private Object doubleValue; + @JsonProperty("textValue") + private String textValue; + @JsonProperty("textValue2") + private Object textValue2; + @JsonProperty("byteArrayRef") + private ByteArrayRefDTO byteArrayRef; + @JsonProperty("cachedValue") + private String cachedValue; + @JsonProperty("deleted") + private Boolean deleted; + @JsonProperty("bytes") + private Object bytes; + @JsonProperty("persistentState") + private PersistentStateDTO persistentState; + @JsonProperty("value") + private String value; + @JsonProperty("readOnly") + private Boolean readOnly; + @JsonProperty("updated") + private Boolean updated; + @JsonProperty("inserted") + private Boolean inserted; + @JsonProperty("idPrefix") + private String idPrefix; + @JsonProperty("revisionNext") + private Integer revisionNext; + + @NoArgsConstructor + @Data + public static class OriginalPersistentStateDTO { + } + + @NoArgsConstructor + @Data + public static class TypeDTO { + @JsonProperty("cachable") + private Boolean cachable; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("readOnly") + private Boolean readOnly; + } + + @NoArgsConstructor + @Data + public static class ByteArrayRefDTO { + @JsonProperty("id") + private Object id; + @JsonProperty("name") + private Object name; + @JsonProperty("deleted") + private Boolean deleted; + } + + @NoArgsConstructor + @Data + public static class PersistentStateDTO { + @JsonProperty("executionId") + private String executionId; + @JsonProperty("scopeId") + private Object scopeId; + @JsonProperty("subScopeId") + private Object subScopeId; + @JsonProperty("textValue") + private String textValue; + @JsonProperty("scopeType") + private Object scopeType; + @JsonProperty("name") + private String name; + @JsonProperty("typeName") + private String typeName; + @JsonProperty("doubleValue") + private Object doubleValue; + @JsonProperty("longValue") + private Object longValue; + @JsonProperty("textValue2") + private Object textValue2; + } + } + } + + @NoArgsConstructor + @Data + public static class UsedVariablesCacheDTO { + } + + @NoArgsConstructor + @Data + public static class TransientVariablesDTO { + } +} diff --git a/src/main/java/com/ruoyi/project/flowable/utils/FlowTaskUtils.java b/src/main/java/com/ruoyi/project/flowable/utils/FlowTaskUtils.java index 6976a61..5a9bdf2 100644 --- a/src/main/java/com/ruoyi/project/flowable/utils/FlowTaskUtils.java +++ b/src/main/java/com/ruoyi/project/flowable/utils/FlowTaskUtils.java @@ -1,18 +1,27 @@ package com.ruoyi.project.flowable.utils; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.project.flowable.common.constant.ProcessConstants; +import com.ruoyi.project.flowable.common.enums.FlowComment; +import com.ruoyi.project.flowable.domain.FlowCommentDto; import com.ruoyi.project.flowable.domain.FlowTaskEntity; +import com.ruoyi.project.flowable.domain.TaskEntity; import com.ruoyi.project.flowable.factory.FlowServiceFactory; -import com.ruoyi.project.flowable.flow.CustomProcessDiagramGenerator; +import com.ruoyi.project.flowable.flow.FlowableUtils; +import com.ruoyi.project.system.domain.SysUser; import com.sun.org.slf4j.internal.Logger; import com.sun.org.slf4j.internal.LoggerFactory; +import io.netty.util.internal.ObjectUtil; +import org.apache.commons.collections.map.HashedMap; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.EndEvent; import org.flowable.bpmn.model.Process; -import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; @@ -20,16 +29,15 @@ import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinitionQuery; import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.task.api.DelegationState; import org.flowable.task.api.Task; import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; import org.springframework.stereotype.Service; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import static com.ruoyi.common.utils.DateUtils.getDate; @@ -45,12 +53,12 @@ public class FlowTaskUtils extends FlowServiceFactory { /** * 查询流程是否存在 * - * @param processDefinitionId 流程id + * @param procDefKey 流程id * @return 是否存在 */ - public boolean exist(String processDefinitionId) { + public boolean exist(String procDefKey) { ProcessDefinitionQuery processDefinitionQuery - = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId); + = repositoryService.createProcessDefinitionQuery().processDefinitionKey(procDefKey); long count = processDefinitionQuery.count(); return count > 0; } @@ -70,30 +78,46 @@ public class FlowTaskUtils extends FlowServiceFactory { /** * 根据流程定义ID启动流程实例 * - * @param procDefId 流程模板ID - * @param variables 流程变量 - * @param userId 用户id + * @param procDefKey 流程定义ID + * @param variables 流程变量 + * @param userId 用户id * @return 流程实例详细信息 */ - public AjaxResult startProcessInstanceById(String userId, String procDefId, Map variables) { + public AjaxResult startFlow(String userId, String procDefKey, Map variables) { try { + if (Objects.isNull(variables)) { + variables = new HashMap<>(); + } if (Objects.isNull(userId)) { return AjaxResult.error("userId为空"); } - if (Objects.isNull(procDefId)) { - return AjaxResult.error("procDefId不能为空"); + if (Objects.isNull(procDefKey)) { + return AjaxResult.error("procDefKey不能为空"); } - if (!exist(procDefId)) { + if (!exist(procDefKey)) { return AjaxResult.error("流程不存在"); } - if (singleResult(procDefId)) { + if (singleResult(procDefKey)) { return AjaxResult.error("流程已被挂起,请先激活流程"); } // 设置流程发起人Id到流程中 identityService.setAuthenticatedUserId(userId); variables.put(ProcessConstants.PROCESS_INITIATOR, userId); - ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefId, variables); - return AjaxResult.success("流程启动成功"); + variables.put(ProcessConstants.FLOWABLE_SKIP_EXPRESSION_ENABLED, true); + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(procDefKey, variables); + Map map = new HashMap<>(); + if (Objects.nonNull(processInstance)) { + map.put("proInsId", processInstance.getProcessInstanceId()); + map.put("startUserId", processInstance.getStartUserId()); + map.put("startTime", processInstance.getStartTime()); + map.put("proDefKey", processInstance.getProcessDefinitionKey()); + map.put("proDefId", processInstance.getDeploymentId()); + map.put("proDefName", processInstance.getProcessDefinitionName()); + map.put("proVariables", processInstance.getProcessVariables()); + } else { + return AjaxResult.error("流程启动错误"); + } + return AjaxResult.success("流程启动成功", map); } catch (Exception e) { logger.error("流程启动错误", e); return AjaxResult.error("流程启动错误"); @@ -103,81 +127,455 @@ public class FlowTaskUtils extends FlowServiceFactory { /** * 根据用户id和流程定义id查询用户发起的流程实例 - * 分页处理 * - * @param userId 用户ID - * @param procDefId 流程定义id + * @param userId 用户ID + * @param proInsId 流程实例ID * @return 集合 */ - public AjaxResult selectFlowTaskUserById(String userId, String procDefId) { + public AjaxResult selectSingleFlow(String userId, String proInsId) { + + if (StringUtils.isBlank(userId)) { + return AjaxResult.error("userId不能为空"); + } + if (StringUtils.isBlank(proInsId)) { + return AjaxResult.error("proInsId不能为空"); + } HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() .startedBy(userId) - .processDefinitionId("") + .processInstanceId(proInsId) .orderByProcessInstanceStartTime() .desc(); - //queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize() - List historicProcessInstances = historicProcessInstanceQuery.listPage(0, 50); - List flowList = new ArrayList<>(); + List historicProcessInstances = historicProcessInstanceQuery.listPage(0, 10); + List> mapList = new ArrayList<>(); for (HistoricProcessInstance hisIns : historicProcessInstances) { - FlowTaskEntity flowTask = new FlowTaskEntity(); - flowTask.setCreateTime(hisIns.getStartTime()); - flowTask.setFinishTime(hisIns.getEndTime()); - flowTask.setProcInsId(hisIns.getId()); + Map map = new HashMap<>(); + map.put("createTime", hisIns.getStartTime()); + map.put("finishTime", hisIns.getEndTime()); + map.put("startUserId", hisIns.getStartUserId()); + map.put("proInsId", hisIns.getId()); // 计算耗时 if (Objects.nonNull(hisIns.getEndTime())) { long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); - flowTask.setDuration(getDate(time)); + map.put("duration", time); } else { long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); - flowTask.setDuration(getDate(time)); + map.put("duration", time); } - // 流程定义信息 - ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() - .processDefinitionId(hisIns.getProcessDefinitionId()) - .singleResult(); - flowTask.setDeployId(pd.getDeploymentId()); - flowTask.setProcDefName(pd.getName()); - flowTask.setProcDefVersion(pd.getVersion()); - flowTask.setCategory(pd.getCategory()); - flowTask.setProcDefVersion(pd.getVersion()); // 当前所处流程 List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); if (CollectionUtils.isNotEmpty(taskList)) { - flowTask.setTaskId(taskList.get(0).getId()); - flowTask.setTaskName(taskList.get(0).getName()); + map.put("taskId", taskList.get(0).getId()); + map.put("taskName", taskList.get(0).getName()); if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { // 当前任务节点办理人信息 - flowTask.setAssigneeId(Long.parseLong(taskList.get(0).getAssignee())); - } - } else { - List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); - flowTask.setTaskId(historicTaskInstance.get(0).getId()); - flowTask.setTaskName(historicTaskInstance.get(0).getName()); - if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { - flowTask.setAssigneeId(Long.parseLong(historicTaskInstance.get(0).getAssignee())); + map.put("assigneeId", taskList.get(0).getAssignee()); } } - flowList.add(flowTask); + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + List> mapHistList = new ArrayList<>(); + for (HistoricTaskInstance hti : historicTaskInstance) { + if (Objects.nonNull(hti.getDurationInMillis())) { + Map mapHist = new HashMap<>(); + mapHist.put("taskId", hti.getId()); + mapHist.put("taskName", hti.getName()); + mapHist.put("assigneeId", hti.getAssignee()); + mapHist.put("durationInMillis", hti.getDurationInMillis()); + List commentList = taskService.getProcessInstanceComments(hti.getProcessInstanceId()); + commentList.forEach(comment -> { + if (hti.getId().equals(comment.getTaskId())) { + Map mapComment = new HashMap<>(); + mapComment.put("comment", FlowCommentDto.builder().type(comment.getType()).comment(comment.getFullMessage()).build()); + mapHist.put("comment", mapComment); + } + }); + mapHistList.add(mapHist); + } + } + // 获取意见评论内容 + + map.put("historicTask", mapHistList); + mapList.add(map); } - return AjaxResult.success(flowList); + return AjaxResult.success(mapList); } + /** + * 流程实例 + * + * @param userId 用户id + * @param proDefKey 流程定义ID + * @param pageSize 当前页码 + * @param pageNum 每页条数 + * @return 分页数据 + */ - public AjaxResult stopProcess(String instanceId) { - List task = taskService.createTaskQuery().processInstanceId(instanceId).list(); + public AjaxResult selectAggregateFlow(String userId, String proDefKey, Integer pageSize, Integer pageNum) { + if (StringUtils.isBlank(userId)) { + return AjaxResult.error("userId不能为空"); + } + if (StringUtils.isBlank(proDefKey)) { + return AjaxResult.error("proDefKey不能为空"); + } + if (Objects.isNull(pageSize)) { + pageSize = 10; + } + if (Objects.isNull(pageNum)) { + pageNum = 1; + } + HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId) + .processDefinitionKey(proDefKey) + .orderByProcessInstanceStartTime() + .desc(); + List historicProcessInstances = historicProcessInstanceQuery.listPage(pageSize * (pageNum - 1), pageSize * pageNum); + List> mapList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + Map map = new HashMap<>(); + map.put("createTime", hisIns.getStartTime()); + map.put("finishTime", hisIns.getEndTime()); + map.put("startUserId", hisIns.getStartUserId()); + map.put("proInsId", hisIns.getId()); + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + map.put("duration", time); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + map.put("duration", time); + } + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + map.put("taskId", taskList.get(0).getId()); + map.put("taskName", taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + map.put("assigneeId", taskList.get(0).getAssignee()); + } + } + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + List> mapHistList = new ArrayList<>(); + for (HistoricTaskInstance hti : historicTaskInstance) { + if (Objects.nonNull(hti.getDurationInMillis())) { + Map mapHist = new HashMap<>(); + mapHist.put("taskId", hti.getId()); + mapHist.put("taskName", hti.getName()); + mapHist.put("assigneeId", hti.getAssignee()); + mapHist.put("durationInMillis", hti.getDurationInMillis()); + mapHistList.add(mapHist); + } + } + map.put("historicTask", mapHistList); + mapList.add(map); + } + return AjaxResult.success(mapList); + } + + /** + * 待办流程实例 + * + * @param userId 用户id + * @param pageSize 当前页码 + * @param pageNum 每页条数 + * @return 分页数据 + */ + public AjaxResult getStayAllFlow(String userId, Integer pageSize, Integer pageNum) { + if (StringUtils.isBlank(userId)) { + return AjaxResult.error("userId不能为空"); + } + if (Objects.isNull(pageSize)) { + pageSize = 10; + } + if (Objects.isNull(pageNum)) { + pageNum = 1; + } + List taskList = taskService.createTaskQuery() + .active() + .taskAssignee(userId) + .orderByTaskCreateTime().desc() + .listPage(pageSize * (pageNum - 1), pageSize); + List> mapList = new ArrayList<>(); + for (Task task : taskList) { + Map map = new HashMap<>(); + map.put("createTime", task.getCreateTime()); + map.put("proInsId", task.getProcessInstanceId()); + map.put("taskId", task.getId()); + map.put("taskName", task.getName()); + map.put("assigneeId", task.getAssignee()); + // 流程发起人信息 + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(task.getProcessInstanceId()) + .singleResult(); + map.put("startUserId", historicProcessInstance.getStartUserId()); + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceEndTime().desc().list(); + List> mapHistList = new ArrayList<>(); + for (HistoricTaskInstance hti : historicTaskInstance) { + if (Objects.nonNull(hti.getDurationInMillis())) { + Map mapHist = new HashMap<>(); + mapHist.put("taskId", hti.getId()); + mapHist.put("taskName", hti.getName()); + mapHist.put("assigneeId", hti.getAssignee()); + mapHist.put("durationInMillis", hti.getDurationInMillis()); + mapHistList.add(mapHist); + } + } + map.put("historicTask", mapHistList); + mapList.add(map); + } + return AjaxResult.success(mapList); + } + + public AjaxResult getStayFlow(String userId, String proDefKey, Integer pageSize, Integer pageNum) { + if (StringUtils.isBlank(userId)) { + return AjaxResult.error("userId不能为空"); + } + if (StringUtils.isBlank(proDefKey)) { + return AjaxResult.error("proDefKey不能为空"); + } + if (Objects.isNull(pageSize)) { + pageSize = 10; + } + if (Objects.isNull(pageNum)) { + pageNum = 1; + } + List taskList = taskService.createTaskQuery() + .active() + .taskAssignee(userId) + .processDefinitionKey(proDefKey) + .orderByTaskCreateTime().desc() + .listPage(pageSize * (pageNum - 1), pageSize); + List> mapList = new ArrayList<>(); + for (Task task : taskList) { + Map map = new HashMap<>(); + map.put("createTime", task.getCreateTime()); + map.put("proInsId", task.getProcessInstanceId()); + map.put("taskId", task.getId()); + map.put("taskName", task.getName()); + map.put("assigneeId", task.getAssignee()); + // 流程发起人信息 + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(task.getProcessInstanceId()) + .singleResult(); + map.put("startUserId", historicProcessInstance.getStartUserId()); + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceEndTime().desc().list(); + List> mapHistList = new ArrayList<>(); + for (HistoricTaskInstance hti : historicTaskInstance) { + if (Objects.nonNull(hti.getDurationInMillis())) { + Map mapHist = new HashMap<>(); + mapHist.put("taskId", hti.getId()); + mapHist.put("taskName", hti.getName()); + mapHist.put("assigneeId", hti.getAssignee()); + mapHist.put("durationInMillis", hti.getDurationInMillis()); + mapHistList.add(mapHist); + } + } + map.put("historicTask", mapHistList); + mapList.add(map); + } + return AjaxResult.success(mapList); + } + + /** + * @param userId + * @param pageSize + * @param pageNum + * @return + */ + public AjaxResult getCompletedAllFlow(String userId, Integer pageSize, Integer pageNum) { + if (StringUtils.isBlank(userId)) { + return AjaxResult.error("userId不能为空"); + } + if (Objects.isNull(pageSize)) { + pageSize = 10; + } + if (Objects.isNull(pageNum)) { + pageNum = 1; + } + HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery() + .includeProcessVariables() + .finished() + .taskAssignee(userId) + .orderByHistoricTaskInstanceEndTime() + .desc(); + List historicTaskInstanceList = taskInstanceQuery.listPage(pageSize * (pageNum - 1), pageSize * pageNum); + List> mapList = new ArrayList<>(); + for (HistoricTaskInstance histTask : historicTaskInstanceList) { + Map map = new HashMap<>(); + // 当前流程信息 + map.put("createTime", histTask.getCreateTime()); + map.put("finishTime", histTask.getEndTime()); + map.put("taskId", histTask.getId()); + map.put("taskName", histTask.getName()); + map.put("assigneeId", histTask.getAssignee()); + map.put("duration", histTask.getDurationInMillis()); + map.put("proDefId", histTask.getProcessDefinitionId()); + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(histTask.getProcessDefinitionId()) + .singleResult(); + map.put("deployId", pd.getDeploymentId()); + map.put("procInsId", histTask.getProcessInstanceId()); + // 流程发起人信息 + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(histTask.getProcessInstanceId()) + .singleResult(); + map.put("startUserId", historicProcessInstance.getStartUserId()); + mapList.add(map); + } + return AjaxResult.success(mapList); + } + + /** + * @param userId + * @param pageSize + * @param pageNum + * @return + */ + public AjaxResult getCompletedFlow(String userId, String proDefKey, Integer pageSize, Integer pageNum) { + if (Objects.isNull(pageSize)) { + pageSize = 10; + } + if (Objects.isNull(pageNum)) { + pageNum = 1; + } + if (StringUtils.isBlank(userId)) { + return AjaxResult.error("userId不能为空"); + } + if (StringUtils.isBlank(proDefKey)) { + return AjaxResult.error("proDefKey不能为空"); + } + HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery() + .includeProcessVariables() + .finished() + .taskAssignee(userId) + .processDefinitionKey(proDefKey) + .orderByHistoricTaskInstanceEndTime() + .desc(); + List historicTaskInstanceList = taskInstanceQuery.listPage(pageSize * (pageNum - 1), pageSize * pageNum); + List> mapList = new ArrayList<>(); + for (HistoricTaskInstance histTask : historicTaskInstanceList) { + Map map = new HashMap<>(); + // 当前流程信息 + map.put("createTime", histTask.getCreateTime()); + map.put("finishTime", histTask.getEndTime()); + map.put("taskId", histTask.getId()); + map.put("taskName", histTask.getName()); + map.put("assigneeId", histTask.getAssignee()); + map.put("duration", histTask.getDurationInMillis()); + map.put("proDefId", histTask.getProcessDefinitionId()); + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(histTask.getProcessDefinitionId()) + .singleResult(); + map.put("deployId", pd.getDeploymentId()); + map.put("procInsId", histTask.getProcessInstanceId()); + // 流程发起人信息 + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(histTask.getProcessInstanceId()) + .singleResult(); + map.put("startUserId", historicProcessInstance.getStartUserId()); + mapList.add(map); + } + return AjaxResult.success(mapList); + } + + public AjaxResult recordFlow(String procInsId) { + if (StringUtils.isBlank(procInsId)) { + return AjaxResult.error("procInsId不能为空"); + } + List> mapList = new ArrayList<>(); + if (StringUtils.isNotBlank(procInsId)) { + List list = historyService + .createHistoricActivityInstanceQuery() + .processInstanceId(procInsId) + .orderByHistoricActivityInstanceStartTime() + .desc().list(); + for (HistoricActivityInstance histIns : list) { + if (StringUtils.isNotBlank(histIns.getTaskId())) { + Map map = new HashMap(); + map.put("taskId", histIns.getTaskId()); + map.put("taskName", histIns.getActivityName()); + map.put("startTime", histIns.getStartTime()); + map.put("endTime", histIns.getEndTime()); + if (StringUtils.isNotBlank(histIns.getAssignee())) { + map.put("assigneeId", histIns.getAssignee()); + } + // 展示审批人员 + List linksForTask = historyService.getHistoricIdentityLinksForTask(histIns.getTaskId()); + StringBuilder stringBuilder = new StringBuilder(); + for (HistoricIdentityLink identityLink : linksForTask) { + // 获选人,候选组/角色(多个) + if ("candidate".equals(identityLink.getType())) { + if (StringUtils.isNotBlank(identityLink.getUserId())) { + stringBuilder.append(identityLink.getUserId()).append(","); + } + if (StringUtils.isNotBlank(identityLink.getGroupId())) { + stringBuilder.append(identityLink.getGroupId()).append(","); + } + } + } + if (StringUtils.isNotBlank(stringBuilder)) { + map.put("candidate", stringBuilder.substring(0, stringBuilder.length() - 1)); + } + map.put("duration", histIns.getDurationInMillis() == null || histIns.getDurationInMillis() == 0 ? null : getDate(histIns.getDurationInMillis())); + // 获取意见评论内容 + List commentList = taskService.getProcessInstanceComments(histIns.getProcessInstanceId()); + commentList.forEach(comment -> { + if (histIns.getTaskId().equals(comment.getTaskId())) { + Map mapComment = new HashMap<>(); + mapComment.put("comment", FlowCommentDto.builder().type(comment.getType()).comment(comment.getFullMessage()).build()); + map.put("comment", mapComment); + } + }); + mapList.add(map); + } + } + } + return AjaxResult.success(mapList); + } + + public AjaxResult approvalFlow(String userId, String taskId, String procInsId, String opinions, Map variables) { + if (StringUtils.isBlank(taskId)) { + return AjaxResult.error("taskId不能为空"); + } + if (StringUtils.isBlank(procInsId)) { + return AjaxResult.error("procInsId不能为空"); + } + if (StringUtils.isBlank(userId)) { + return AjaxResult.error("userId不能为空"); + } + if (Objects.isNull(variables)) { + variables = new HashMap<>(); + } + Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskId, procInsId, FlowComment.DELEGATE.getType(), opinions); + taskService.resolveTask(taskId, variables); + } else { + taskService.addComment(taskId, procInsId, FlowComment.NORMAL.getType(), opinions); + taskService.setAssignee(taskId, userId); + taskService.complete(taskId, variables); + } + return AjaxResult.success(); + } + + public AjaxResult stopFlow(String procInstId) { + List task = taskService.createTaskQuery().processInstanceId(procInstId).list(); if (CollectionUtils.isEmpty(task)) { - AjaxResult.error("流程未启动或已执行完成,取消申请失败"); + return AjaxResult.error("流程未启动或已执行完成,取消申请失败"); } // 获取当前流程实例 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(instanceId) + .processInstanceId(procInstId) .singleResult(); BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); if (Objects.nonNull(bpmnModel)) { Process process = bpmnModel.getMainProcess(); List endNodes = process.findFlowElementsOfType(EndEvent.class, false); if (CollectionUtils.isNotEmpty(endNodes)) { - // 获取当前流程最后一个节点 String endId = endNodes.get(0).getId(); List executions = runtimeService.createExecutionQuery() .parentId(processInstance.getProcessInstanceId()).list(); @@ -188,54 +586,154 @@ public class FlowTaskUtils extends FlowServiceFactory { .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); } } + return AjaxResult.success(); + } + public AjaxResult revokeFlow(String proInstId) { + if (StringUtils.isBlank(proInstId)) { + return AjaxResult.error("proInsId不能为空"); + } + + Task task = taskService.createTaskQuery().processInstanceId(proInstId).singleResult(); + if (task == null) { + return AjaxResult.error("流程未启动或已执行完成,取消申请失败"); + } + + SysUser loginUser = SecurityUtils.getLoginUser().getUser(); + List htiList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(task.getProcessInstanceId()) + .orderByTaskCreateTime() + .asc() + .list(); + String myTaskId = null; + HistoricTaskInstance myTask = null; + for (HistoricTaskInstance hti : htiList) { + if (loginUser.getUserId().toString().equals(hti.getAssignee())) { + myTaskId = hti.getId(); + myTask = hti; + break; + } + } + if (null == myTaskId) { + return AjaxResult.error("该任务非当前用户提交,无法撤回"); + } + + String processDefinitionId = myTask.getProcessDefinitionId(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId); + + String myActivityId = null; + List haiList = historyService.createHistoricActivityInstanceQuery() + .executionId(myTask.getExecutionId()).finished().list(); + for (HistoricActivityInstance hai : haiList) { + if (myTaskId.equals(hai.getTaskId())) { + myActivityId = hai.getActivityId(); + break; + } + } + FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId); + Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult(); + String activityId = execution.getActivityId(); + FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId); + //记录原活动方向 + List oriSequenceFlows = new ArrayList<>(flowNode.getOutgoingFlows()); + return AjaxResult.success(); + } + + public AjaxResult processVariables(String taskId) { + if (StringUtils.isBlank(taskId)) { + return AjaxResult.error("taskId不能为空"); + } + // 流程变量 + HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().includeProcessVariables().finished().taskId(taskId).singleResult(); + if (Objects.nonNull(historicTaskInstance)) { + return AjaxResult.success(historicTaskInstance.getProcessVariables()); + } else { + Map variables = taskService.getVariables(taskId); + return AjaxResult.success(variables); + } + + + } + + public AjaxResult rejectFlow(String taskId, String targetKey, String options) { + if (StringUtils.isBlank(taskId)) { + return AjaxResult.error("taskId不能为空"); + } + if (StringUtils.isBlank(targetKey)) { + return AjaxResult.error("targetKey不能为空"); + } + if (StringUtils.isBlank(options)) { + return AjaxResult.error("options不能为空"); + } + if (taskService.createTaskQuery().taskId(taskId).singleResult().isSuspended()) { + return AjaxResult.error("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(targetKey)) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, targetKey, null, null); + if (!isSequential) { + return AjaxResult.error("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), options)); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetKey).changeState(); + } catch (FlowableObjectNotFoundException e) { + return AjaxResult.error("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + return AjaxResult.error("无法取消或开始活动"); + } return AjaxResult.success(); } - /** - * 获取流程过程图 - * - * @param processId 流程实例id - * @return 图片流 - */ - public InputStream diagram(String processId) { - String processDefinitionId; - // 获取当前的流程实例 - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult(); - // 如果流程已经结束,则得到结束节点 - if (Objects.isNull(processInstance)) { - HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult(); - - processDefinitionId = pi.getProcessDefinitionId(); - } else {// 如果流程没有结束,则取当前活动节点 - // 根据流程实例ID获得当前处于活动状态的ActivityId合集 - ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult(); - processDefinitionId = pi.getProcessDefinitionId(); - } - - // 获得活动的节点 - List highLightedFlowList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processId).orderByHistoricActivityInstanceStartTime().asc().list(); - - List highLightedFlows = new ArrayList<>(); - List highLightedNodes = new ArrayList<>(); - //高亮线 - for (HistoricActivityInstance tempActivity : highLightedFlowList) { - if ("sequenceFlow".equals(tempActivity.getActivityType())) { - //高亮线 - highLightedFlows.add(tempActivity.getActivityId()); - } else { - //高亮节点 - highLightedNodes.add(tempActivity.getActivityId()); - } - } - //获取流程图 - BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId); - ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration(); - //获取自定义图片生成器 - ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator(); - return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodes, highLightedFlows, configuration.getActivityFontName(), - configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true); - - } } diff --git a/src/main/resources/mybatis/flow/FlowMapper.xml b/src/main/resources/mybatis/flow/FlowMapper.xml index 5e381e4..3db6907 100644 --- a/src/main/resources/mybatis/flow/FlowMapper.xml +++ b/src/main/resources/mybatis/flow/FlowMapper.xml @@ -18,10 +18,21 @@ ACT_RE_PROCDEF rp LEFT JOIN ACT_RE_DEPLOYMENT rd ON rp.deployment_id_ = rd.id_ + rp.deployment_id_ IN ( + SELECT d.deployment_id_ + FROM ACT_RE_PROCDEF d + INNER JOIN ( + SELECT key_, MAX(version_) AS max_version + FROM ACT_RE_PROCDEF + GROUP BY key_ + ) m ON d.key_ = m.key_ AND d.version_ = m.max_version + ) + and rd.name_ like concat('%', #{name}, '%') order by rd.deploy_time_ desc + \ No newline at end of file