From 8c86b66584776ea8177ca221195802c4c23558c2 Mon Sep 17 00:00:00 2001 From: zhangp <732291000@qq.com> Date: Fri, 11 Nov 2022 16:51:57 +0800 Subject: [PATCH] no message --- .gitignore | 25 +++ pom.xml | 13 ++ .../activiti/controller/BpmnController.java | 191 ++++++++++++++++++ .../boot/activiti/dto/DeploymentDTO.java | 23 +++ .../zilber/boot/activiti/dto/InstanceDTO.java | 33 +++ .../boot/activiti/dto/PageQueryDTO.java | 23 +++ .../boot/activiti/dto/TaskCompleteDTO.java | 28 +++ .../boot/activiti/dto/TaskQueryDTO.java | 22 ++ .../activiti/service/DefinitionService.java | 164 +++++++++++++++ .../boot/activiti/service/HistoryService.java | 54 +++++ .../activiti/service/InstanceService.java | 167 +++++++++++++++ .../boot/activiti/service/TaskService.java | 123 +++++++++++ .../zilber/boot/activiti/vo/DefinitionVO.java | 34 ++++ .../boot/activiti/vo/HistoricTaskVO.java | 74 +++++++ .../boot/activiti/vo/InstanceProgressVO.java | 54 +++++ .../zilber/boot/activiti/vo/InstanceVO.java | 48 +++++ .../zilber/boot/activiti/vo/SimpleTaskVO.java | 43 ++++ .../com/zilber/boot/activiti/vo/TaskVO.java | 62 ++++++ .../boot/framework/config/SecurityConfig.java | 2 +- src/main/resources/application-druid.yml | 4 +- src/main/resources/application.yml | 9 +- src/main/resources/banner.txt | 24 --- src/main/resources/bpmn/leave.bpmn20.xml | 40 ++++ 23 files changed, 1231 insertions(+), 29 deletions(-) create mode 100644 .gitignore create mode 100644 src/main/java/com/zilber/boot/activiti/controller/BpmnController.java create mode 100644 src/main/java/com/zilber/boot/activiti/dto/DeploymentDTO.java create mode 100644 src/main/java/com/zilber/boot/activiti/dto/InstanceDTO.java create mode 100644 src/main/java/com/zilber/boot/activiti/dto/PageQueryDTO.java create mode 100644 src/main/java/com/zilber/boot/activiti/dto/TaskCompleteDTO.java create mode 100644 src/main/java/com/zilber/boot/activiti/dto/TaskQueryDTO.java create mode 100644 src/main/java/com/zilber/boot/activiti/service/DefinitionService.java create mode 100644 src/main/java/com/zilber/boot/activiti/service/HistoryService.java create mode 100644 src/main/java/com/zilber/boot/activiti/service/InstanceService.java create mode 100644 src/main/java/com/zilber/boot/activiti/service/TaskService.java create mode 100644 src/main/java/com/zilber/boot/activiti/vo/DefinitionVO.java create mode 100644 src/main/java/com/zilber/boot/activiti/vo/HistoricTaskVO.java create mode 100644 src/main/java/com/zilber/boot/activiti/vo/InstanceProgressVO.java create mode 100644 src/main/java/com/zilber/boot/activiti/vo/InstanceVO.java create mode 100644 src/main/java/com/zilber/boot/activiti/vo/SimpleTaskVO.java create mode 100644 src/main/java/com/zilber/boot/activiti/vo/TaskVO.java delete mode 100644 src/main/resources/banner.txt create mode 100644 src/main/resources/bpmn/leave.bpmn20.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..631d03f --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ +target + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar +*.iml +.idea + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7c6a984..c4e125d 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ 4.1.2 2.3 0.9.1 + @@ -236,6 +237,18 @@ ${jwt.version} + + + org.activiti + activiti-spring-boot-starter + 7.1.0.M6 + + + + org.projectlombok + lombok + 1.18.24 + diff --git a/src/main/java/com/zilber/boot/activiti/controller/BpmnController.java b/src/main/java/com/zilber/boot/activiti/controller/BpmnController.java new file mode 100644 index 0000000..04058b1 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/controller/BpmnController.java @@ -0,0 +1,191 @@ +package com.zilber.boot.activiti.controller; + + +import com.github.pagehelper.PageInfo; +import com.zilber.boot.activiti.dto.DeploymentDTO; +import com.zilber.boot.activiti.dto.InstanceDTO; +import com.zilber.boot.activiti.dto.PageQueryDTO; +import com.zilber.boot.activiti.dto.TaskQueryDTO; +import com.zilber.boot.activiti.service.*; +import com.zilber.boot.activiti.vo.DefinitionVO; +import com.zilber.boot.activiti.vo.HistoricTaskVO; +import com.zilber.boot.activiti.vo.InstanceVO; +import com.zilber.boot.activiti.vo.TaskVO; +import com.zilber.boot.utils.AjaxResult; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.List; + +@Validated +@RestController +@RequestMapping("/bpmn") +@Api(tags = "工作流接口") +@Slf4j +public class BpmnController { + + @Resource + private DefinitionService definitionService; + + @Resource + private InstanceService instanceService; + + @Resource + private TaskService taskService; + + @Resource + private HistoryService historyService; + + @PostMapping("/definition/upload") + @ApiOperation("上传流程图") + public AjaxResult uploadAndDeployment( + @RequestParam @ApiParam("名称") String name, + @RequestParam @ApiParam("上传的文件") MultipartFile file) { + definitionService.uploadAndDeployment(name, file); + return AjaxResult.success(); + } + + @PostMapping("/definition") + @ApiOperation("保存流程图") + public AjaxResult saveAndDeployment(@RequestBody @Valid DeploymentDTO dto) { + definitionService.saveAndDeployment(dto.getName(), dto.getXml()); + return AjaxResult.success(); + } + + @GetMapping("/definition/page") + @ApiOperation("查询流程定义列表") + public AjaxResult getDefinitionPage(@Valid PageQueryDTO dto) { + PageInfo page = new PageInfo<>(definitionService.getDefinitionPage(dto)); + return AjaxResult.success(page); + } + + @GetMapping("/definition/list/by/{key}") + @ApiOperation("查询指定Key的流程定义所有列表") + public AjaxResult getVersionList( + @PathVariable @NotBlank String key) { + return AjaxResult.success(definitionService.getVersionList(key)); + } + + @GetMapping("/definition/by/key/{key}") + @ApiOperation("查询流程图,XML字符串格式") + public AjaxResult getDeploymentXMLByKey( + @PathVariable @ApiParam("流程定义Key") @NotBlank String key) { + String xmlString = definitionService.getDeploymentXML(key); + HashMap map = new HashMap<>(); + map.put("xml", xmlString); + return AjaxResult.success(map); + } + + @GetMapping("/definition/by/{id}") + @ApiOperation("查询流程图,XML字符串格式") + public AjaxResult getDeploymentXML( + @PathVariable @ApiParam("流程部署ID") @NotBlank String id, + @RequestParam @ApiParam("资源文件名") @NotBlank String name) { + String xmlString = definitionService.getDeploymentXML(id, name); + HashMap map = new HashMap<>(); + map.put("xml", xmlString); + return AjaxResult.success(map); + } + + @GetMapping("/definition/file/{id}") + @ApiOperation("查询流程图,XML文件格式") + public void getDeploymentFile( + HttpServletResponse response, + @PathVariable @ApiParam("流程部署ID") @NotBlank String id, + @RequestParam @ApiParam("资源文件名") @NotBlank String name) { + InputStream is = definitionService.getDeploymentStream(id, name); + OutputStream os; + try { + os = response.getOutputStream(); + IOUtils.copy(is, os); + + is.close(); + os.close(); + } catch (IOException e) { + log.error("查看流程文件失败", e); + } + } + + @DeleteMapping("/definition/by/{id}") + @ApiOperation("删除流程定义") + public AjaxResult deleteDefinitionById( + @PathVariable @ApiParam("流程部署ID") @NotBlank String id) { + definitionService.deleteById(id); + return AjaxResult.success(); + } + + @GetMapping("/instance/page") + @ApiOperation("查询流程实例列表") + public AjaxResult getInstancePage(@Valid PageQueryDTO dto) { + PageInfo page = new PageInfo<>(instanceService.getInstancePage(dto)); + return AjaxResult.success(page); + } + + @PostMapping("/instance") + @ApiOperation("启动流程实例") + public AjaxResult startInstance(@RequestBody @Valid InstanceDTO dto) { + instanceService.start(dto); + return AjaxResult.success(); + } + + @PutMapping("/instance/suspend/by/{id}") + @ApiOperation("挂起流程实例") + public AjaxResult suspendInstance(@PathVariable @ApiParam("流程实例ID") @NotBlank String id) { + instanceService.suspend(id); + return AjaxResult.success(); + } + + @PutMapping("/instance/resume/by/{id}") + @ApiOperation("激活/重启流程实例") + public AjaxResult resumeInstance(@PathVariable @ApiParam("流程实例ID") @NotBlank String id) { + instanceService.resume(id); + return AjaxResult.success(); + } + + @DeleteMapping("/instance/by/{id}") + @ApiOperation("取消流程实例") + public AjaxResult deleteInstanceById(@PathVariable @ApiParam("流程实例ID") @NotBlank String id) { + instanceService.cancelById(id); + return AjaxResult.success(); + } + + @GetMapping("/instance/variables/by/{id}") + @ApiOperation("查询流程参数") + public AjaxResult instanceVariables(@PathVariable @ApiParam("流程实例ID") @NotBlank String id) { + return AjaxResult.success(instanceService.getVariables(id)); + } + + @GetMapping("/task/page") + @ApiOperation("查看待办任务") + public AjaxResult taskPage(@Valid TaskQueryDTO dto) { + PageInfo page = new PageInfo<>(taskService.taskPage(dto)); + return AjaxResult.success(page); + } + + @GetMapping("/historic/task/page") + @ApiOperation("查看历史任务") + public AjaxResult historicTaskPage(@Valid TaskQueryDTO dto) { + PageInfo page = new PageInfo<>(historyService.historicTaskPage(dto)); + return AjaxResult.success(page); + } + + @GetMapping("/historic/task/list") + @ApiOperation("任务实例历史") + public List getHistoryListByInstanceId( + @RequestParam @ApiParam("流程实例ID") @NotBlank String instanceId) { + return historyService.getListOfInstance(instanceId); + } +} diff --git a/src/main/java/com/zilber/boot/activiti/dto/DeploymentDTO.java b/src/main/java/com/zilber/boot/activiti/dto/DeploymentDTO.java new file mode 100644 index 0000000..0ca7217 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/dto/DeploymentDTO.java @@ -0,0 +1,23 @@ +package com.zilber.boot.activiti.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; + +@Getter +@Setter +@ApiModel("流程部署提交") +public class DeploymentDTO { + + @NotBlank(message = "名称不能为空") + @ApiModelProperty(value = "名称", required = true) + private String name; + + @NotBlank(message = "bpmn文件xml不能为空") + @ApiModelProperty(value = "bpmn文件xml", required = true) + private String xml; + +} diff --git a/src/main/java/com/zilber/boot/activiti/dto/InstanceDTO.java b/src/main/java/com/zilber/boot/activiti/dto/InstanceDTO.java new file mode 100644 index 0000000..115b9d4 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/dto/InstanceDTO.java @@ -0,0 +1,33 @@ +package com.zilber.boot.activiti.dto; + + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.Map; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel("启动流程实例") +public class InstanceDTO { + + @NotBlank + @ApiModelProperty(value = "流程定义Key") + @Length(max = 32) + private String procDefKey; + + @Length(max = 24) + @ApiModelProperty(value = "发起人") + private String createBy; + + @ApiModelProperty(value = "流程变量") + private Map variables; + +} diff --git a/src/main/java/com/zilber/boot/activiti/dto/PageQueryDTO.java b/src/main/java/com/zilber/boot/activiti/dto/PageQueryDTO.java new file mode 100644 index 0000000..82f309f --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/dto/PageQueryDTO.java @@ -0,0 +1,23 @@ +package com.zilber.boot.activiti.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Positive; +import javax.validation.constraints.PositiveOrZero; +import java.io.Serializable; + +@Getter +@Setter +public class PageQueryDTO implements Serializable { + + @ApiModelProperty("每页记录数") + @Positive(message = "分页数量必须为正整数") + private Integer pageSize = 20; + + @ApiModelProperty("当前页数") + @PositiveOrZero(message = "分页数不正确") + private Integer pageNo = 0; +} diff --git a/src/main/java/com/zilber/boot/activiti/dto/TaskCompleteDTO.java b/src/main/java/com/zilber/boot/activiti/dto/TaskCompleteDTO.java new file mode 100644 index 0000000..94ff33c --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/dto/TaskCompleteDTO.java @@ -0,0 +1,28 @@ +package com.zilber.boot.activiti.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotNull; +import java.util.Map; + +@Getter +@Setter +@ApiModel("完成流程任务") +public class TaskCompleteDTO { + + @NotNull(message = "请选择审批结果") + @ApiModelProperty("审批状态:1-通过 0-拒绝(其他可以根据业务自行添加)") + private Integer status; + + @Length(max = 512) + @ApiModelProperty("意见") + private String remark; + + @ApiModelProperty(value = "流程变量") + private Map variables; + +} diff --git a/src/main/java/com/zilber/boot/activiti/dto/TaskQueryDTO.java b/src/main/java/com/zilber/boot/activiti/dto/TaskQueryDTO.java new file mode 100644 index 0000000..8ca6f30 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/dto/TaskQueryDTO.java @@ -0,0 +1,22 @@ +package com.zilber.boot.activiti.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@ApiModel("任务查询") +public class TaskQueryDTO extends PageQueryDTO { + + @ApiModelProperty("流程定义Key") + private String procDefKey; + + @ApiModelProperty("任务执行人") + private String assignee; + + @ApiModelProperty("流程实例ID") + private String procInstId; + +} diff --git a/src/main/java/com/zilber/boot/activiti/service/DefinitionService.java b/src/main/java/com/zilber/boot/activiti/service/DefinitionService.java new file mode 100644 index 0000000..9ed78b6 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/service/DefinitionService.java @@ -0,0 +1,164 @@ +package com.zilber.boot.activiti.service; + +import com.zilber.boot.activiti.dto.PageQueryDTO; +import com.zilber.boot.activiti.vo.DefinitionVO; +import com.zilber.boot.exception.ServiceException; +import org.activiti.engine.RepositoryService; +import org.activiti.engine.repository.ProcessDefinition; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOUtils; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; +import java.util.zip.ZipInputStream; + +@Service +public class DefinitionService { + + @Autowired + private RepositoryService repositoryService; + + /** + * 上传并部署流程定义 + * + * @param name 流程定义名称 + * @param file 上传的文件 + */ + public void uploadAndDeployment(String name, MultipartFile file) { + try { + String filename = file.getOriginalFilename(); + String extension = FilenameUtils.getExtension(filename); + InputStream fileInputStream = file.getInputStream(); + if ("zip".equals(extension)) { + ZipInputStream zip = new ZipInputStream(fileInputStream); + repositoryService.createDeployment() + .addZipInputStream(zip) + .name(name) + .deploy(); + } else { + repositoryService.createDeployment() + .addInputStream(filename, fileInputStream) + .name(name) + .deploy(); + } + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + public List getDefinitionPage(PageQueryDTO dto) { + List list = repositoryService.createProcessDefinitionQuery() + .latestVersion().listPage(dto.getPageNo() * dto.getPageSize(), dto.getPageSize()); + list.sort((y, x) -> x.getVersion() - y.getVersion()); + + return list.stream().map(processDefinition -> { + DefinitionVO vo = new DefinitionVO(); + BeanUtils.copyProperties(processDefinition, vo); + return vo; + }).collect(Collectors.toList()); + } + + /** + * 根据流程定义Key获取历史版本 + * + * @param key 流程定义Key + * @return 历史版本列表 + */ + public List getVersionList(String key) { + List list = repositoryService.createProcessDefinitionQuery() + .processDefinitionKey(key) + .orderByProcessDefinitionVersion().desc() + .list(); + return list.stream().map(processDefinition -> { + DefinitionVO vo = new DefinitionVO(); + BeanUtils.copyProperties(processDefinition, vo); + return vo; + }).collect(Collectors.toList()); + } + + /** + * 在线绘制流程图保存 + * + * @param name 流程名称 + * @param xml xml数据 + */ + public void saveAndDeployment(String name, String xml) { + repositoryService.createDeployment() + .addString("bpmnjs.bpmn", xml) + .name(name) + .deploy(); + } + + /** + * 获取流程图(流) + * + * @param deploymentId 流程部署ID + * @param resourceName 资源文件名称 + * @return 数据流 + */ + public InputStream getDeploymentStream(String deploymentId, String resourceName) { + return repositoryService.getResourceAsStream(deploymentId, resourceName); + } + + /** + * 获取流程图xml + * + * @param definitionKey 流程定义Key + * @return xml字符串 + */ + public String getDeploymentXML(String definitionKey) { + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() + .processDefinitionKey(definitionKey) + .latestVersion() + .singleResult(); + return getDeploymentXML(processDefinition.getDeploymentId(), processDefinition.getResourceName()); + } + + /** + * 获取流程图xml + * + * @param deploymentId 流程部署ID + * @param resourceName 资源文件名称 + * @return xml字符串 + */ + public String getDeploymentXML(String deploymentId, String resourceName) { + try { + InputStream inputStream = repositoryService.getResourceAsStream(deploymentId, resourceName); + StringWriter writer = new StringWriter(); + IOUtils.copy(inputStream, writer, StandardCharsets.UTF_8.name()); + return writer.toString(); + } catch (IOException e) { + throw new ServiceException(e.getMessage()); + } + } + + /** + * 获取流程定义 + * + * @param processDefinitionId 流程定义ID + * @return + */ + public ProcessDefinition getById(String processDefinitionId) { + return repositoryService.createProcessDefinitionQuery() + .processDefinitionId(processDefinitionId) + .singleResult(); + } + + /** + * 根据流程定义id删除流程定义 + * + * @param id 流程定义id + */ + public void deleteById(String id) { + repositoryService.deleteDeployment(id); + } +} diff --git a/src/main/java/com/zilber/boot/activiti/service/HistoryService.java b/src/main/java/com/zilber/boot/activiti/service/HistoryService.java new file mode 100644 index 0000000..c0c57ed --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/service/HistoryService.java @@ -0,0 +1,54 @@ +package com.zilber.boot.activiti.service; + + +import com.zilber.boot.activiti.dto.TaskQueryDTO; +import com.zilber.boot.activiti.vo.HistoricTaskVO; +import org.activiti.engine.history.HistoricTaskInstance; +import org.activiti.engine.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; + +import java.util.stream.Collectors; + + +@Service(value = "HistoryService_") +public class HistoryService { + + @Resource + private org.activiti.engine.HistoryService historyService; + + public List historicTaskPage(TaskQueryDTO dto) { + HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery() + .finished() + .orderByHistoricTaskInstanceEndTime() + .desc(); + if (!StringUtils.isEmpty(dto.getAssignee())) { + taskInstanceQuery.taskAssignee(dto.getAssignee()); + } + if (!StringUtils.isEmpty(dto.getProcDefKey())) { + taskInstanceQuery.processDefinitionKey(dto.getProcDefKey()); + } + if (!StringUtils.isEmpty(dto.getProcInstId())) { + taskInstanceQuery.processInstanceId(dto.getProcInstId()); + } + List tasks = taskInstanceQuery.listPage(dto.getPageNo() * dto.getPageSize(), dto.getPageSize()); + return HistoricTaskVO.merge(tasks); + + } + + public List getListOfInstance(String instanceId) { + List list = historyService.createHistoricTaskInstanceQuery() + .orderByHistoricTaskInstanceStartTime() + .asc() + .processInstanceId(instanceId) + .list(); + return list.stream() + .map(HistoricTaskVO::merge) + .collect(Collectors.toList()); + + } +} diff --git a/src/main/java/com/zilber/boot/activiti/service/InstanceService.java b/src/main/java/com/zilber/boot/activiti/service/InstanceService.java new file mode 100644 index 0000000..4d7a2d0 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/service/InstanceService.java @@ -0,0 +1,167 @@ +package com.zilber.boot.activiti.service; + + +import com.zilber.boot.activiti.dto.InstanceDTO; +import com.zilber.boot.activiti.dto.PageQueryDTO; +import com.zilber.boot.activiti.vo.InstanceProgressVO; +import com.zilber.boot.activiti.vo.InstanceVO; +import org.activiti.engine.RuntimeService; +import org.activiti.engine.TaskService; +import org.activiti.engine.impl.persistence.entity.EntityManager; +import org.activiti.engine.runtime.ProcessInstance; +import org.activiti.engine.runtime.ProcessInstanceQuery; +import org.activiti.engine.task.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class InstanceService { + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private TaskService taskService; + + public ProcessInstance getById(String processInstanceId) { + return runtimeService.createProcessInstanceQuery() + .processInstanceId(processInstanceId) + .singleResult(); + } + + public List getInstancePage(PageQueryDTO dto) { + ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery(); + return InstanceVO.merge(query.listPage(dto.getPageNo() * dto.getPageSize(), dto.getPageSize())); + } + + /** + * 启动流程实例并完成第一个任务 + * + * @param dto 启动流程实例必要参数 + */ + public void start(InstanceDTO dto) { + Map variables = dto.getVariables(); + if (variables == null) { + variables = new HashMap<>(); + } + + // 设置默认的流程变量 + variables.put("assignee0", dto.getCreateBy()); + //variables.put("procDefKey", dto.getProcDefKey()); + + // 启动流程实例 + ProcessInstance processInstance = runtimeService.startProcessInstanceByKey( + dto.getProcDefKey(), /*dto.getBusinessId() +*/ "", variables); + + // 执行第一个任务 + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); + if (task != null) { + if (task.getAssignee() == null) { + taskService.claim(task.getId(), dto.getCreateBy()); + } + taskService.complete(task.getId()); + } + + } + + /** + * 查询指定流程进度 + * + * @param instanceId 流程实例ID + * @return 进度列表 + */ + public List getInstanceProgress(String instanceId) { + String sql = "select\n" + + " t1.ACT_ID_ as actId,\n" + + " t1.TASK_ID_ as taskId,\n" + + " t1.ACT_NAME_ as actName,\n" + + " t1.ACT_TYPE_ as actType,\n" + + " t1.PROC_INST_ID_ as procInstId,\n" + + " t1.DURATION_ as duration,\n" + + " t1.ACT_INST_STATE_ as state,\n" + + " t1.START_TIME_ as startTime,\n" + + " t1.END_TIME_ as endTime,\n" + + " t1.ASSIGNEE_ as assignee,\n" + + " t2.MESSAGE_ as remark,\n" + + " t3.TEXT_ as operators\n" + + "from ACT_HI_ACTINST as t1\n" + + " left join ACT_HI_COMMENT as t2 on t1.TASK_ID_ = t2.TASK_ID_\n" + + " left join ACT_HI_VARINST as t3 on t1.ID_ = t3.ACT_INST_ID_ and t3.NAME_ = ?2\n" + + "where t1.PROC_INST_ID_ = ?1\n" + + " " + + "and t1.ACT_TYPE_ = 'userTask'\n" + + "order by t1.START_TIME_ desc, NOT ISNULL(t1.END_TIME_), t1" + + ".END_TIME_ desc"; + + /*Query nativeQuery = entityManager.createNativeQuery(sql); + nativeQuery.setParameter(1, instanceId); + nativeQuery.setParameter(2, CamundaConstants.OPERATORS); + return nativeQuery + .unwrap(NativeQueryImpl.class) + .setResultTransformer(Transformers.aliasToBean(InstanceProgressVO.class)) + .list();*/ + return null; + } + + /** + * 挂起流程实例 + * + * @param id 流程实例id + */ + public void suspend(String id) { + runtimeService.suspendProcessInstanceById(id); + } + + /** + * 激活流程实例 + * + * @param id 流程实例id + */ + public void resume(String id) { + runtimeService.activateProcessInstanceById(id); + } + + /** + * 结束流程实例(审批失败) + * + * @param id 流程实例id + * @param reason 失败原因 + */ + public void failEndById(String id, String reason) { + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(id).singleResult(); + if (processInstance != null) { + // 审批失败,状态为 INTERNALLY_TERMINATED + runtimeService.deleteProcessInstance(processInstance.getId(), reason); + } + } + + /** + * 取消流程实例 + * + * @param id 流程实例id + */ + public void cancelById(String id) { + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(id).singleResult(); + if (processInstance != null) { + // 手动取消,状态为 EXTERNALLY_TERMINATED + runtimeService.deleteProcessInstance(processInstance.getId(), "手动取消"); + } + } + + /** + * 获取流程变量 + * + * @param id 流程实例id + * @return 流程变量map + */ + public Map getVariables(String id) { + return runtimeService.getVariables(id); + } + +} diff --git a/src/main/java/com/zilber/boot/activiti/service/TaskService.java b/src/main/java/com/zilber/boot/activiti/service/TaskService.java new file mode 100644 index 0000000..edb444a --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/service/TaskService.java @@ -0,0 +1,123 @@ +package com.zilber.boot.activiti.service; + +import com.zilber.boot.activiti.dto.TaskCompleteDTO; +import com.zilber.boot.activiti.dto.TaskQueryDTO; +import com.zilber.boot.activiti.vo.TaskVO; +import com.zilber.boot.exception.ServiceException; +import org.activiti.engine.task.Task; +import org.activiti.engine.task.TaskQuery; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service(value = "TaskService_") +public class TaskService { + + @Resource + private org.activiti.engine.TaskService taskService; + + @Resource + private InstanceService instanceService; + + public List taskPage(TaskQueryDTO dto) { + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .orderByTaskCreateTime() + .desc(); + if (!StringUtils.isEmpty(dto.getAssignee())) { + taskQuery.taskAssignee(dto.getAssignee()); + } + if (!StringUtils.isEmpty(dto.getProcDefKey())) { + taskQuery.processDefinitionKey(dto.getProcDefKey()); + } + if (!StringUtils.isEmpty(dto.getProcInstId())) { + taskQuery.processInstanceId(dto.getProcInstId()); + } + List tasks = taskQuery.listPage(dto.getPageNo() * dto.getPageSize(), dto.getPageSize()); + return TaskVO.merge(tasks); + } + + public void complete(String userCode, String taskId, TaskCompleteDTO dto) { + Task task = taskService.createTaskQuery() + .taskAssignee(userCode) + .taskId(taskId) + .singleResult(); + + if (task == null) { + throw new ServiceException("没有任务"); + } + + if (task.getAssignee() == null) { + taskService.claim(taskId, userCode); + } + if (!StringUtils.isEmpty(dto.getRemark())) { + //taskService.createComment(taskId, task.getProcessInstanceId(), dto.getRemark()); + } + Map variables = dto.getVariables(); + if (dto.getVariables() == null) { + variables = new HashMap<>(); + } + if (dto.getStatus() == 0) { + // TODO: 多实例并行任务,需要按照业务处理,是否一个人审批不通过,就都不通过 + taskService.setVariable(taskId, "status", 0); + instanceService.failEndById(task.getProcessInstanceId(), dto.getRemark()); + } else { + variables.put("status", dto.getStatus()); + taskService.complete(taskId, variables); + } + } + + /*public List getSimpleTasks(List businessIds, DefinitionKey definitionKey) { + String sql = "SELECT\n" + + " t2.ID_ as taskId,\n" + + " t2.NAME_ as taskName,\n" + + " t2.DESCRIPTION_ as taskDescription,\n" + + " t2.TASK_DEF_KEY_ as taskDefinitionKey,\n" + + " t2.ASSIGNEE_ as assignee,\n" + + " t2.CREATE_TIME_ as taskTime,\n" + + " t2.PRIORITY_ as priority,\n" + + " t2.SUSPENSION_STATE_ as suspensionState,\n" + + " t1.business_id as businessId\n" + + "FROM bpm_business as t1\n" + + " LEFT JOIN ACT_RU_TASK as t2 on t1.proc_inst_id = t2.PROC_INST_ID_\n" + + "WHERE t1.business_id in (?1) and t1.proc_def_key = ?2"; + + Query nativeQuery = entityManager.createNativeQuery(sql); + nativeQuery.setParameter(1, businessIds); + nativeQuery.setParameter(2, definitionKey.getValue()); + return nativeQuery + .unwrap(NativeQueryImpl.class) + .setResultTransformer(Transformers.aliasToBean(io.izn.iec.camunda.vo.SimpleTaskVO.class)) + .list(); + } + + public List getSimpleTasks(List businessIds, List definitionKey) { + List collect = definitionKey.stream().map(DefinitionKey::getValue).collect(Collectors.toList()); + String sql = "SELECT\n" + + " t2.ID_ as taskId,\n" + + " t2.NAME_ as taskName,\n" + + " t2.DESCRIPTION_ as taskDescription,\n" + + " t2.TASK_DEF_KEY_ as taskDefinitionKey,\n" + + " t2.ASSIGNEE_ as assignee,\n" + + " t2.CREATE_TIME_ as taskTime,\n" + + " t2.PRIORITY_ as priority,\n" + + " t2.SUSPENSION_STATE_ as suspensionState,\n" + + " t1.business_id as businessId\n" + + "FROM bpm_business as t1\n" + + " LEFT JOIN ACT_RU_TASK as t2 on t1.proc_inst_id = t2.PROC_INST_ID_\n" + + "WHERE t1.business_id in (?1) and t1.proc_def_key in (?2)"; + + Query nativeQuery = entityManager.createNativeQuery(sql); + nativeQuery.setParameter(1, businessIds); + nativeQuery.setParameter(2, collect); + return nativeQuery + .unwrap(NativeQueryImpl.class) + .setResultTransformer(Transformers.aliasToBean(io.izn.iec.camunda.vo.SimpleTaskVO.class)) + .list(); + }*/ +} diff --git a/src/main/java/com/zilber/boot/activiti/vo/DefinitionVO.java b/src/main/java/com/zilber/boot/activiti/vo/DefinitionVO.java new file mode 100644 index 0000000..c6b2252 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/vo/DefinitionVO.java @@ -0,0 +1,34 @@ +package com.zilber.boot.activiti.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@ApiModel("流程定义") +public class DefinitionVO { + + @ApiModelProperty("流程定义ID") + private String id; + + @ApiModelProperty("流程定义名称") + private String name; + + @ApiModelProperty("流程定义描述") + private String description; + + @ApiModelProperty("流程定义Key") + private String key; + + @ApiModelProperty("流程定义资源名称") + private String resourceName; + + @ApiModelProperty("流程部署ID") + private String deploymentId; + + @ApiModelProperty("版本号") + private Integer version; + +} diff --git a/src/main/java/com/zilber/boot/activiti/vo/HistoricTaskVO.java b/src/main/java/com/zilber/boot/activiti/vo/HistoricTaskVO.java new file mode 100644 index 0000000..d8c85d7 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/vo/HistoricTaskVO.java @@ -0,0 +1,74 @@ +package com.zilber.boot.activiti.vo; + + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import org.activiti.engine.history.HistoricTaskInstance; +import org.springframework.beans.BeanUtils; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Getter +@Setter +@ApiModel("历史任务") +public class HistoricTaskVO { + + @ApiModelProperty("任务ID") + private String taskId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务描述") + private String taskDescription; + + @ApiModelProperty("任务定义Key") + private String taskDefinitionKey; + + @ApiModelProperty("执行人") + private String assignee; + + @ApiModelProperty("任务开始时间") + private Date startTime; + + @ApiModelProperty("任务结束时间") + private Date endTime; + + @ApiModelProperty("任务优先级") + private Integer priority; + + @ApiModelProperty("任务执行时长") + private Long duration; + + @ApiModelProperty("任务删除原因") + private String deleteReason; + + public static List merge(List tasks) { + return tasks.stream() + .map(HistoricTaskVO::merge) + .collect(Collectors.toList()); + } + + public static HistoricTaskVO merge(HistoricTaskInstance task) { + HistoricTaskVO vo = new HistoricTaskVO(); + vo.setTaskId(task.getId()); + vo.setTaskName(task.getName()); + vo.setTaskDescription(task.getDescription()); + vo.setTaskDefinitionKey(task.getTaskDefinitionKey()); + vo.setAssignee(task.getAssignee()); + vo.setStartTime(task.getStartTime()); + vo.setEndTime(task.getEndTime()); + vo.setPriority(task.getPriority()); + vo.setDuration(task.getDurationInMillis()); + vo.setDeleteReason(task.getDeleteReason()); + return vo; + } + + +} diff --git a/src/main/java/com/zilber/boot/activiti/vo/InstanceProgressVO.java b/src/main/java/com/zilber/boot/activiti/vo/InstanceProgressVO.java new file mode 100644 index 0000000..24a8539 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/vo/InstanceProgressVO.java @@ -0,0 +1,54 @@ +package com.zilber.boot.activiti.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.Date; + +@Getter +@Setter +@ApiModel("审批进度") +public class InstanceProgressVO implements Serializable { + + @ApiModelProperty("活动ID") + private String actId; + + @ApiModelProperty("任务ID") + private String taskId; + + @ApiModelProperty("活动名称") + private String actName; + + // 'userTask', 'startEvent', 'noneEndEvent' + @ApiModelProperty("活动类型") + private String actType; + + @ApiModelProperty("流程实例ID") + private String procInstId; + + @ApiModelProperty("任务执行时长") + private BigInteger duration; + + // ActivityInstanceState + @ApiModelProperty("活动状态:0-default 1-scopeComplete 2-canceled 3-starting 4-ending") + private Integer state; + + @ApiModelProperty("开始时间") + private Date startTime; + + @ApiModelProperty("结束时间") + private Date endTime; + + @ApiModelProperty("执行人") + private String assignee; + + @ApiModelProperty("审批意见") + private String remark; + + @ApiModelProperty("额外操作项") + private String operators; +} diff --git a/src/main/java/com/zilber/boot/activiti/vo/InstanceVO.java b/src/main/java/com/zilber/boot/activiti/vo/InstanceVO.java new file mode 100644 index 0000000..c26b206 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/vo/InstanceVO.java @@ -0,0 +1,48 @@ +package com.zilber.boot.activiti.vo; + + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import org.activiti.engine.runtime.ProcessInstance; + +import java.util.List; + +import java.util.stream.Collectors; + +@Getter +@Setter +@ApiModel("流程实例") +public class InstanceVO { + + @ApiModelProperty("是否挂起:1-正常 2-挂起") + private Integer suspensionState; + + @ApiModelProperty("流程定义Key") + private String procDefKey; + + @ApiModelProperty("流程定义ID") + private String procDefId; + + @ApiModelProperty("流程实例ID") + private String procInstId; + + @ApiModelProperty("备注") + private String remark; + + @ApiModelProperty("发起人") + private String createBy; + + public static List merge(List instances) { + return instances.stream().map(instance -> { + InstanceVO vo = new InstanceVO(); + vo.setSuspensionState(instance.isSuspended() ? 2 : 1); + vo.setProcDefKey(instance.getProcessDefinitionKey()); + vo.setProcDefId(instance.getProcessDefinitionId()); + vo.setProcInstId(instance.getProcessInstanceId()); + vo.setCreateBy(instance.getStartUserId()); + return vo; + }).collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/zilber/boot/activiti/vo/SimpleTaskVO.java b/src/main/java/com/zilber/boot/activiti/vo/SimpleTaskVO.java new file mode 100644 index 0000000..385a129 --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/vo/SimpleTaskVO.java @@ -0,0 +1,43 @@ +package com.zilber.boot.activiti.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; + +@Getter +@Setter +@ApiModel("流程任务(简单数据结构)") +public class SimpleTaskVO implements Serializable { + + @ApiModelProperty("任务ID") + private String taskId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务描述") + private String taskDescription; + + @ApiModelProperty("任务定义Key") + private String taskDefinitionKey; + + @ApiModelProperty("执行人") + private String assignee; + + @ApiModelProperty("任务开始时间") + private Date taskTime; + + @ApiModelProperty("任务优先级") + private Integer priority; + + @ApiModelProperty("是否为挂起状态:1-否 2-是") + private Integer suspensionState; + + @ApiModelProperty("业务ID") + private Integer businessId; + +} diff --git a/src/main/java/com/zilber/boot/activiti/vo/TaskVO.java b/src/main/java/com/zilber/boot/activiti/vo/TaskVO.java new file mode 100644 index 0000000..838488a --- /dev/null +++ b/src/main/java/com/zilber/boot/activiti/vo/TaskVO.java @@ -0,0 +1,62 @@ +package com.zilber.boot.activiti.vo; + + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import org.activiti.engine.task.Task; +import org.springframework.beans.BeanUtils; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Getter +@Setter +@ApiModel("流程任务") +public class TaskVO { + + @ApiModelProperty("任务ID") + private String taskId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务描述") + private String taskDescription; + + @ApiModelProperty("任务定义Key") + private String taskDefinitionKey; + + @ApiModelProperty("执行人") + private String assignee; + + @ApiModelProperty("任务开始时间") + private Date taskTime; + + @ApiModelProperty("任务优先级") + private Integer priority; + + @ApiModelProperty("是否为挂起状态:1-否 2-是") + private Integer suspensionState; + + public static List merge(List tasks) { + return tasks.stream().map(task -> { + TaskVO vo = new TaskVO(); + vo.setTaskId(task.getId()); + vo.setTaskName(task.getName()); + vo.setTaskDescription(task.getDescription()); + vo.setTaskDefinitionKey(task.getTaskDefinitionKey()); + vo.setAssignee(task.getAssignee()); + vo.setTaskTime(task.getCreateTime()); + vo.setPriority(task.getPriority()); + vo.setSuspensionState(task.isSuspended() ? 2 : 1); + return vo; + }).collect(Collectors.toList()); + } + + +} diff --git a/src/main/java/com/zilber/boot/framework/config/SecurityConfig.java b/src/main/java/com/zilber/boot/framework/config/SecurityConfig.java index 08667b6..5b8a35a 100644 --- a/src/main/java/com/zilber/boot/framework/config/SecurityConfig.java +++ b/src/main/java/com/zilber/boot/framework/config/SecurityConfig.java @@ -112,7 +112,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter .antMatchers("/login", "/register", "/captchaImage").anonymous() // 静态资源,可匿名访问 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js","/**/*.jpg","/**/*.png","/zilbervue/admin/", "/profile/**").permitAll() - .antMatchers("/swagger-ui.html","/swagger-ui", "/v3","/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() + .antMatchers("/bpmn/**","/swagger-ui.html","/swagger-ui", "/v3","/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() diff --git a/src/main/resources/application-druid.yml b/src/main/resources/application-druid.yml index 3cae6f8..c96e804 100644 --- a/src/main/resources/application-druid.yml +++ b/src/main/resources/application-druid.yml @@ -6,9 +6,9 @@ spring: druid: # 主库数据源 master: - url: jdbc:mysql://localhost:3306/zilberboot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + url: jdbc:mysql://39.100.74.100:3306/zilberboot?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root - password: root + password: root@123456 # 从库数据源 slave: # 从数据源开关/默认关闭 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 475747b..6410efe 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -68,13 +68,13 @@ spring: # redis 配置 redis: # 地址 - host: localhost + host: 39.100.74.100 # 端口,默认为6379 port: 6379 # 数据库索引 database: 0 # 密码 - password: + password: redis@ly1234 # 连接超时时间 timeout: 10s lettuce: @@ -87,6 +87,11 @@ spring: max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms + activiti: + history-level: full + db-history-used: true + check-process-definitions: false + deployment-mode: never-fail # token配置 token: diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt deleted file mode 100644 index cf2c596..0000000 --- a/src/main/resources/banner.txt +++ /dev/null @@ -1,24 +0,0 @@ -Application Version: ${zilberboot.version} -Spring Boot Version: ${spring-boot.version} -//////////////////////////////////////////////////////////////////// -// _ooOoo_ // -// o8888888o // -// 88" . "88 // -// (| ^_^ |) // -// O\ = /O // -// ____/`---'\____ // -// .' \\| |// `. // -// / \\||| : |||// \ // -// / _||||| -:- |||||- \ // -// | | \\\ - /// | | // -// | \_| ''\---/'' | | // -// \ .-\__ `-` ___/-. / // -// ___`. .' /--.--\ `. . ___ // -// ."" '< `.___\_<|>_/___.' >'"". // -// | | : `- \`.;`\ _ /`;.`/ - ` : | | // -// \ \ `-. \_ __\ /__ _/ .-` / / // -// ========`-.____`-.___\_____/___.-`____.-'======== // -// `=---=' // -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // -// 佛祖保佑 永不宕机 永无BUG // -//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/main/resources/bpmn/leave.bpmn20.xml b/src/main/resources/bpmn/leave.bpmn20.xml new file mode 100644 index 0000000..b26af30 --- /dev/null +++ b/src/main/resources/bpmn/leave.bpmn20.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +