update(REQ-2516) - 利用 javaparser 生成可用接口

This commit is contained in:
wangli 2024-06-11 17:59:50 +08:00
parent 15323a6752
commit ce23fcbc69
22 changed files with 1161 additions and 421 deletions

View File

@ -65,11 +65,11 @@
<artifactId>workflow-engine-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<!--<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>workflow-auto-gen</artifactId>
<version>1.3.3-SNAPSHOT</version>
</dependency>
</dependency>-->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>workflow-engine-server</artifactId>
@ -136,11 +136,11 @@
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<!--<path>
<groupId>cn.axzo.workflow</groupId>
<artifactId>workflow-auto-gen</artifactId>
<version>1.3.3-SNAPSHOT</version>
</path>
</path>-->
</annotationProcessorPaths>
</configuration>
</plugin>

View File

@ -23,10 +23,10 @@
<artifactId>workflow-engine-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<!--<dependency>
<groupId>cn.axzo.workflow</groupId>
<artifactId>workflow-auto-gen</artifactId>
</dependency>
</dependency>-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.client.feign.bpmn;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.definition.BpmnProcessDefinitionUpdateDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelUpdateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessDefinitionPageDTO;
@ -24,6 +25,7 @@ import javax.validation.constraints.NotNull;
* @since 2023/9/21 16:25
*/
@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@Manageable
public interface ProcessDefinitionApi {

View File

@ -1,6 +1,8 @@
package cn.axzo.workflow.client.feign.bpmn;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
@ -16,7 +18,6 @@ import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAd
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import cn.axzo.workflow.generate.annotition.GenService;
import cn.azxo.framework.common.model.CommonResponse;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Operation;
@ -35,13 +36,14 @@ import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
/**
* 流程实例 API
*
* @author wangli
* @since 2023/9/21 16:26
*/
@GenService(genPkgName = "cn.axzo.workflow.starter.api")
@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
public interface ProcessInstanceApi {
/**
@ -51,6 +53,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "查询所有的审批流")
@PostMapping("/api/process/instance/page/all")
@Manageable
CommonResponse<BpmPageResult<BpmnProcessInstanceAdminPageItemVO>> getAllProcessInstancePage(@Validated @RequestBody BpmnProcessInstanceAdminPageReqVO dto);
/**
@ -58,6 +61,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "我发起的审批列表")
@PostMapping("/api/process/instance/page/my")
@Manageable
CommonResponse<BpmPageResult<BpmnProcessInstancePageItemVO>> getMyProcessInstancePage(@Validated @RequestBody BpmnProcessInstanceMyPageReqVO dto);
/**
@ -73,6 +77,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "创建审批流程, MQ 触发规则:1. 当前流程实例会依次触发 process-instance-created 和 process-instance-started 事件,2. 第一个审批任务会依次触发 process-task-assigned 和 process-task-created 事件")
@PostMapping("/api/process/instance/create")
@InvokeMode(SYNC)
CommonResponse<String> createProcessInstance(@Validated @RequestBody BpmnProcessInstanceCreateDTO dto);
/**
@ -83,6 +88,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "创建审批流程并带上表单")
@PostMapping("/api/process/instance/form/create")
@Manageable
CommonResponse<String> createProcessInstanceWith(@Validated @RequestBody BpmnProcessInstanceCreateWithFormDTO dto);
/**
@ -149,6 +155,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "更新指定流程定义的版本的状态, 处于 suspended 状态的流程模型将不能再发起实例")
@PutMapping("/api/process/instance/status/update")
@Manageable
CommonResponse<Boolean> updateProcessStatus(@NotBlank(message = "流程定义 ID 不能为空") @RequestParam String processDefinitionId,
@NotNull(message = "状态不能为空") @RequestParam Integer status);
@ -161,6 +168,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "获取审批流程实例的运行图")
@GetMapping("/api/process/instance/graphical")
@Manageable
CommonResponse<ObjectNode> processInstanceGraphical(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@ -171,6 +179,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "推断指定流程实例的所有节点执行顺序")
@GetMapping("/api/process/instance/node/forecasting")
@Manageable
CommonResponse<List<ProcessNodeDetailVO>> processInstanceNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@ -183,6 +192,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "推断指定流程实例的过滤掉部分节点执行顺序")
@GetMapping("/api/process/instance/node/filter/forecasting")
@Manageable
CommonResponse<List<ProcessNodeDetailVO>> processInstanceFilterNodeForecast(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId,
@RequestParam(required = false, defaultValue = "false") Boolean allNode,
@ -207,6 +217,7 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "查询实例的租户集合")
@GetMapping("/api/process/instance/tenant/ids")
@Manageable
CommonResponse<List<String>> getTenantIds();
/**
@ -216,5 +227,6 @@ public interface ProcessInstanceApi {
*/
@Operation(summary = "校验指定流程实例下,是否存在指定的审批人")
@PostMapping("/api/process/instance/check/approver")
@Manageable
CommonResponse<Boolean> checkInstanceApprover(@Validated @RequestBody BpmnProcessInstanceCheckApproverDTO dto);
}

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.client.feign.bpmn;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelUpdateDTO;
@ -29,6 +30,7 @@ import java.util.List;
* @since 2023/9/21 15:47
*/
@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@Manageable
public interface ProcessModelApi {
/**

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.client.feign.bpmn;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO;
@ -17,7 +18,6 @@ import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstance
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskDonePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import cn.axzo.workflow.generate.annotition.GenService;
import cn.azxo.framework.common.model.CommonResponse;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.cloud.openfeign.FeignClient;
@ -41,7 +41,6 @@ import java.util.Map;
* @since 2023/9/21 16:26
*/
@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@GenService(genPkgName = "cn.axzo.workflow.starter.api")
public interface ProcessTaskApi {
/**
@ -49,6 +48,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "待审核列表")
@GetMapping("/api/process/task/page/todo")
@Manageable
CommonResponse<BpmPageResult<BpmnTaskTodoPageItemVO>> getTodoTaskPage(@Validated @RequestBody BpmnTaskPageSearchDTO dto);
/**
@ -56,6 +56,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "已完成的审批列表")
@GetMapping("/api/process/task/page/done")
@Manageable
CommonResponse<BpmPageResult<BpmnTaskDonePageItemVO>> getDoneTaskPage(@Validated @RequestBody BpmnTaskPageSearchDTO dto);
/**
@ -65,6 +66,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "获取指定流程实例的审批过程信息")
@GetMapping("/api/process/task/list/flat")
@Manageable
CommonResponse<List<BpmnHistoricTaskInstanceVO>> getTaskListFlatByProcessInstanceId(@NotBlank(message = "流程实例 ID " +
"不能为空") @RequestParam String processInstanceId, @Nullable @RequestParam(required = false) String tenantId);
@ -75,6 +77,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "获取指定流程实例的审批过程信息")
@GetMapping("/api/process/task/list/group")
@Manageable
CommonResponse<List<BpmnHistoricTaskInstanceGroupVO>> getTaskListGroupByProcessInstanceId(@NotBlank(message =
"流程实例 ID 不能为空") @RequestParam String processInstanceId, @Nullable @RequestParam(required = false) String tenantId);
@ -83,6 +86,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "获取实例正在审核的人列表")
@GetMapping("/api/process/task/active/list")
@Manageable
CommonResponse<List<BpmnTaskInstanceVO>> getActiveTasksByProcessInstanceId(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@NotBlank(message = "租户不能为空") @RequestParam String tenantId);
@ -171,6 +175,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "添加附件")
@PostMapping("/api/process/task/attachment")
@Manageable
CommonResponse<Void> addAttachment(@Validated @RequestBody BpmnTaskAttachmentDTO dto);
/**
@ -191,6 +196,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "审批流程催办")
@PostMapping("/api/process/task/remind")
@Manageable
CommonResponse<Boolean> remindTask(@Validated @RequestBody BpmnTaskRemindDTO dto);
/**
@ -220,6 +226,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "根据实例 ID 和自然人 ID 查询对应待处理的任务 ID")
@GetMapping("/api/process/task/find")
@Manageable
CommonResponse<String> findTaskIdByInstanceIdAndPersonId(@RequestParam(required = false) @NotBlank(message = "流程实例 ID 不能为空") String processInstanceId,
@RequestParam(required = false) @NotBlank(message = "自然人 ID 不能为空") String personId);
@ -230,6 +237,7 @@ public interface ProcessTaskApi {
*/
@Operation(summary = "根据实例 ID列表 和自然人 ID 查询对应待处理的任务 ID")
@GetMapping("/api/process/task/batch/find")
@Manageable
CommonResponse<Map<String, String>> findTaskIdByInstanceIdsAndPersonId(@RequestParam(required = false) @NotEmpty(message = "流程实例 ID列表 不能为空") List<String> processInstanceIds,
@RequestParam(required = false) @NotBlank(message = "自然人 ID 不能为空") String personId);
}

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.client.feign.bpmn;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.RestBpmnProcessVariable;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.cloud.openfeign.FeignClient;
@ -17,6 +18,7 @@ import javax.validation.constraints.NotBlank;
* 流程变量api
*/
@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@Manageable
public interface ProcessVariableApi {
/**

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.client.feign.manage;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.category.CategoryConfigCreateDTO;
import cn.axzo.workflow.common.model.request.category.CategoryConfigSearchDTO;
import cn.axzo.workflow.common.model.request.category.CategoryCreateDTO;
@ -9,7 +10,6 @@ import cn.axzo.workflow.common.model.request.category.CategoryUpdateDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.category.CategoryConfigItemVO;
import cn.axzo.workflow.common.model.response.category.CategoryItemVO;
import cn.axzo.workflow.generate.annotition.Management;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
@ -33,7 +33,7 @@ import java.util.List;
*/
@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@Management
@Manageable
public interface ProcessCategoryApi {
/**

View File

@ -1,7 +1,7 @@
package cn.axzo.workflow.client.feign.manage;
import cn.axzo.workflow.client.config.CommonFeignConfiguration;
import cn.axzo.workflow.generate.annotition.Management;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.cloud.openfeign.FeignClient;
@ -19,7 +19,7 @@ import java.util.List;
*/
@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class)
@Management
@Manageable
public interface ProcessConfigApi {
/**

View File

@ -217,7 +217,7 @@ public class TestController {
RestBpmnProcessVariable variable = new RestBpmnProcessVariable();
variable.setName("testVar");
variable.setValue("testValue");
workflowCoreService.createVariable(processInstanceId, variable);
// workflowCoreService.createVariable(processInstanceId, variable);
return CommonResponse.success(null);
}

View File

@ -19,10 +19,10 @@
<groupId>cn.axzo.workflow</groupId>
<artifactId>workflow-engine-api</artifactId>
</dependency>
<dependency>
<!--<dependency>
<groupId>cn.axzo.workflow</groupId>
<artifactId>workflow-auto-gen</artifactId>
</dependency>
</dependency>-->
<dependency>
<groupId>cn.axzo.framework.rocketmq</groupId>
<artifactId>axzo-common-rocketmq</artifactId>

View File

@ -1,11 +1,18 @@
package cn.axzo.workflow.starter.api;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.model.request.bpmn.RestBpmnProcessVariable;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCarbonCopyDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.util.ThreadUtil;
@ -15,7 +22,6 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
@ -28,44 +34,233 @@ import java.util.Map;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC;
import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC;
/**
* 模拟生成的受限访问的接口定义
*
* @author wangli
* @since 2024/5/28 16:12
* Workflow Engine Starter Core Service
*/
@FeignClient(name = "workflow-engine-starter",
url = "${axzo.service.workflow-engine:workflow-engine:8080}",
configuration = WorkflowEngineStarterFeignConfiguration.class)
@FeignClient(name = "workflow-engine-starter", url = "${axzo.service.workflow-engine:workflow-engine:8080}", configuration = WorkflowEngineStarterFeignConfiguration.class)
public interface WorkflowCoreService {
/**
* 业务节点唤醒
* <p>
* TODO 接口需要合并但需要考虑客户端与服务端不同版本间如何兼容
*/
@GetMapping("/api/process/activity/old/trigger")
Boolean trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId);
@Operation(summary = "业务节点唤醒")
@GetMapping("/api/process/activity/trigger")
Boolean trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId, @RequestParam(required = false, defaultValue = "false") Boolean async);
/**
* 业务节点设置审批人, 不支持重复设置
*
* @param dto
* @return
*/
@PostMapping("/api/process/activity/assignee/set")
@Operation(summary = "业务节点设置审批人,不支持重复调用设置审批人,需一次性传入所有审批人")
Boolean setAssignee(@Validated @RequestBody BpmnActivitySetAssigneeDTO dto);
/**
* 创建审批流程
*
* <pre>
* MQ 触发规则:
* 1. 当前流程实例会依次触发 process-instance-created process-instance-started 事件
* 2. 第一个审批任务会依次触发 process-task-assigned process-task-created 事件
* </pre>
*
* @param dto {@link BpmnProcessInstanceCreateDTO}
*/
@Operation(summary = "创建审批流程, MQ 触发规则:1. 当前流程实例会依次触发 process-instance-created 和 process-instance-started 事件,2. 第一个审批任务会依次触发 process-task-assigned 和 process-task-created 事件")
@PostMapping("/api/process/instance/create")
@InvokeMode(SYNC)
String createProcessInstance(@Validated @RequestBody BpmnProcessInstanceCreateDTO dto);
@Operation(summary = "同意MQ 触发规则:1. 当前审批任务会依次触发 process-task-completed 和 process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)2.1. 下一级审批任务会依次触发 process-task-assigned 和 process-task-created 事件2.2. 流程实例正常结束会触发 process-instance-completed 事件")
@PostMapping("/api/process/task/approve")
Boolean approveTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
/**
* 发起人主动撤回审核
*
* <pre>
* MQ 触发规则:
* 1. 当前流程实例中现存的任务会依次触发 process-task-deleted 事件
* 2. 当前流程实例会触发 process-instance-cancelled 事件
* </pre>
*
* @param dto {@link BpmnProcessInstanceCancelDTO}
* @return
*/
@Operation(summary = "发起人主动撤回审核,MQ 触发规则:1. 当前流程实例中现存的任务会依次触发 process-task-deleted 事件,2. 当前流程实例会触发 process-instance-cancelled 事件")
@DeleteMapping("/api/process/instance/cancel")
Boolean cancelProcessInstance(@Validated @RequestBody BpmnProcessInstanceCancelDTO dto);
@Operation(summary = "获得流程实例")
@GetMapping("/api/process/instance/get")
BpmnProcessInstanceVO getProcessInstanceVO(@Validated @RequestBody BpmnProcessInstanceQueryDTO dto);
@Operation(summary = "获取指定流程实例的流程变量")
@GetMapping("/api/process/instance/cooperation-org")
Map<String, Object> getProcessVariables(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@Operation(summary = "为指定流程新增变量")
@GetMapping("/api/process/variable/create/{executionId}")
void createVariable(@PathVariable @NotBlank(message = "流程实例 ID 不能为空") String executionId,
@RequestBody @Validated RestBpmnProcessVariable restVariable);
/**
* 中止流程实例
*
* @param dto
* @return
*/
@Operation(summary = "中止流程实例")
@DeleteMapping("/api/process/instance/abort")
Boolean abortProcessInstance(@Validated @RequestBody BpmnProcessInstanceAbortDTO dto);
/**
* 批量中止流程实例
*
* @param dtos
* @return
*/
@Operation(summary = "批量中止流程实例")
@DeleteMapping("/api/process/instance/batch/abort")
BatchOperationResultVO batchAbortProcessInstance(@Validated @RequestBody List<BpmnProcessInstanceAbortDTO> dtos);
/**
* 抄送流程实例
*
* @param dto
* @return
*/
@Operation(summary = "抄送流程实例")
@PostMapping("/api/process/instance/carbon-copy")
Boolean carbonCopyProcessInstance(@Validated @RequestBody BpmnProcessInstanceCarbonCopyDTO dto);
/**
* 获得流程实例
*
* @param dto {@link BpmnProcessInstanceQueryDTO} 可根据 Id,BusinessKey进行查询
* @return 流程实例, 租户Id不必传
*/
@Operation(summary = "获得流程实例")
@GetMapping("/api/process/instance/get")
BpmnProcessInstanceVO getProcessInstanceVO(@Validated @RequestBody BpmnProcessInstanceQueryDTO dto);
/**
* 获取指定流程实例的流程变量
*
* @param processInstanceId
* @param tenantId
* @return
*/
@Operation(summary = "获取指定流程实例的流程变量")
@GetMapping("/api/process/instance/cooperation-org")
Map<String, Object> getProcessVariables(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId, @Nullable @RequestParam(required = false) String tenantId);
@GetMapping("/api/process/job/dead-letter/resume")
Void executeDeadLetterJobAction(@RequestParam(required = false) String jobId, @RequestParam(required = false) String procInstId);
/**
* 同意
*
* <pre>
* MQ 触发规则:
* 1. 当前审批任务会依次触发 process-task-completed process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,
* 如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)
* 2.1. 下一级审批任务会依次触发 process-task-assigned process-task-created 事件
* 2.2. 流程实例正常结束会触发 process-instance-completed 事件
* </pre>
*/
@Operation(summary = "同意MQ 触发规则:1. 当前审批任务会依次触发 process-task-completed 和 process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)2.1. 下一级审批任务会依次触发 process-task-assigned 和 process-task-created 事件2.2. 流程实例正常结束会触发 process-instance-completed 事件")
@PostMapping("/api/process/task/approve")
Boolean approveTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
/**
* 批量同意
*
* @param dtos
* @return
*/
@Operation(summary = "批量同意")
@PostMapping("/api/process/task/batch/approve")
BatchOperationResultVO batchApproveTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
/**
* 驳回
*
* <pre>
* MQ 触发规则:
* 1. 当前审批任务会触发 process-task-deleted 事件
* 2. 当前流程实例会触发 process-instance-rejected 事件
* </pre>
*/
@Operation(summary = "驳回MQ 触发规则1. 当前审批任务会触发 process-task-deleted 事件, 2. 当前流程实例会触发 process-instance-rejected 事件")
@PostMapping("/api/process/task/reject")
Boolean rejectTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
/**
* 批量驳回
*
* @param dtos 批量请求参数
* @return
*/
@PostMapping("/api/process/task/batch/reject")
BatchOperationResultVO batchRejectTask(@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
/**
* 转交
*
* @param dto
* @return
*/
@Operation(summary = "直接修改审批任务的审批人")
@PostMapping("/api/process/task/transfer")
Boolean transferTask(@Validated @RequestBody BpmnTaskTransferDTO dto);
/**
* 批量转交
*
* @param dtos
* @return
*/
@Operation(summary = "批量修改审批任务的审批人")
@PostMapping("/api/process/task/batch/transfer")
BatchOperationResultVO batchTransferTask(@Validated @RequestBody List<BpmnTaskTransferDTO> dtos);
/**
* 评论
*
* @param dto 评论请求参数
* @return
*/
@Operation(summary = "审批流程评论")
@PostMapping("/api/process/task/comment")
Boolean commentTask(@Validated @RequestBody BpmnTaskCommentDTO dto);
/**
* 加签
*
* @param dto 加签请求参数
* @return
*/
@Operation(summary = "审批流程加签")
@PostMapping("/api/process/task/countersign")
Boolean countersignTask(@Validated @RequestBody BpmnTaskCountersignDTO dto);
/**
* 暂停流程任务,并创建机器人节点,等待业务推动
*
* @param dto
* @return 返回机器人节点任务 ID
*/
@Operation(summary = "创建机器人节点, 暂停流程任务")
@PostMapping("/api/process/task/robot/create")
String createRobotTask(@Validated @RequestBody BpmnRobotTaskCreateDTO dto);
/**
* 完成机器人节点
*
* @param dto
* @return
*/
@Operation(summary = "完成机器人节点, 继续流程任务")
@PostMapping("/api/process/task/robot/complete")
Boolean completeRobotTask(@Validated @RequestBody BpmnRobotTaskCompleteDTO dto);
/**
* 强制使用异步模式调用该方法请在调用真实方法前调用该方法
* <pre>
* workflowCoreService.async().createProcessInstance();
* </pre>
*/
default WorkflowCoreService sync() {
ThreadUtil.set(SYNC);
return this;
@ -75,5 +270,4 @@ public interface WorkflowCoreService {
ThreadUtil.set(ASYNC);
return this;
}
}

View File

@ -1,299 +0,0 @@
// --auto generated by workflow auto-gen plugin--
package cn.axzo.workflow.starter.api;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAdminPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCarbonCopyDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCheckApproverDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateWithFormDTO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceMyPageReqVO;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceQueryDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstancePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceGroupVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskDonePageItemVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO;
import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO;
import cn.azxo.framework.common.model.CommonResponse;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.oas.annotations.Operation;
import java.lang.Boolean;
import java.lang.Integer;
import java.lang.Object;
import java.lang.String;
import java.lang.Void;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
public interface WorkflowCoreService_Gen {
@Operation(
summary = "待审核列表"
)
@GetMapping("/api/process/task/page/todo")
CommonResponse<BpmPageResult<BpmnTaskTodoPageItemVO>> getTodoTaskPage(
@Validated @RequestBody BpmnTaskPageSearchDTO dto);
@Operation(
summary = "已完成的审批列表"
)
@GetMapping("/api/process/task/page/done")
CommonResponse<BpmPageResult<BpmnTaskDonePageItemVO>> getDoneTaskPage(
@Validated @RequestBody BpmnTaskPageSearchDTO dto);
@Operation(
summary = "获取指定流程实例的审批过程信息"
)
@GetMapping("/api/process/task/list/flat")
CommonResponse<List<BpmnHistoricTaskInstanceVO>> getTaskListFlatByProcessInstanceId(
@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@Operation(
summary = "获取指定流程实例的审批过程信息"
)
@GetMapping("/api/process/task/list/group")
CommonResponse<List<BpmnHistoricTaskInstanceGroupVO>> getTaskListGroupByProcessInstanceId(
@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@Operation(
summary = "获取实例正在审核的人列表"
)
@GetMapping("/api/process/task/active/list")
CommonResponse<List<BpmnTaskInstanceVO>> getActiveTasksByProcessInstanceId(
@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@NotBlank(message = "租户不能为空") @RequestParam String tenantId);
@Operation(
summary = "同意MQ 触发规则:1. 当前审批任务会依次触发 process-task-completed 和 process-task-deleted 事件(如果有下一级审批,则会触发第 2.1 点中的事件,如果当前审核任务最后一级审批,则会触发第 2.2 点中的事件)2.1. 下一级审批任务会依次触发 process-task-assigned 和 process-task-created 事件2.2. 流程实例正常结束会触发 process-instance-completed 事件"
)
@PostMapping("/api/process/task/approve")
CommonResponse<Boolean> approveTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
@Operation(
summary = "批量同意"
)
@PostMapping("/api/process/task/batch/approve")
CommonResponse<BatchOperationResultVO> batchApproveTask(
@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
@Operation(
summary = "驳回MQ 触发规则1. 当前审批任务会触发 process-task-deleted 事件, 2. 当前流程实例会触发 process-instance-rejected 事件"
)
@PostMapping("/api/process/task/reject")
CommonResponse<Boolean> rejectTask(@Validated @RequestBody BpmnTaskAuditDTO dto);
@PostMapping("/api/process/task/batch/reject")
CommonResponse<BatchOperationResultVO> batchRejectTask(
@Validated @RequestBody List<BpmnTaskAuditDTO> dtos);
@Operation(
summary = "直接修改审批任务的审批人"
)
@PostMapping("/api/process/task/transfer")
CommonResponse<Boolean> transferTask(@Validated @RequestBody BpmnTaskTransferDTO dto);
@Operation(
summary = "批量修改审批任务的审批人"
)
@PostMapping("/api/process/task/batch/transfer")
CommonResponse<BatchOperationResultVO> batchTransferTask(
@Validated @RequestBody List<BpmnTaskTransferDTO> dtos);
@Operation(
summary = "审批流程评论"
)
@PostMapping("/api/process/task/comment")
CommonResponse<Boolean> commentTask(@Validated @RequestBody BpmnTaskCommentDTO dto);
@Operation(
summary = "添加附件"
)
@PostMapping("/api/process/task/attachment")
CommonResponse<Void> addAttachment(@Validated @RequestBody BpmnTaskAttachmentDTO dto);
@Operation(
summary = "审批流程加签"
)
@PostMapping("/api/process/task/countersign")
CommonResponse<Boolean> countersignTask(@Validated @RequestBody BpmnTaskCountersignDTO dto);
@Operation(
summary = "审批流程催办"
)
@PostMapping("/api/process/task/remind")
CommonResponse<Boolean> remindTask(@Validated @RequestBody BpmnTaskRemindDTO dto);
@Operation(
summary = "创建机器人节点, 暂停流程任务"
)
@PostMapping("/api/process/task/robot/create")
CommonResponse<String> createRobotTask(@Validated @RequestBody BpmnRobotTaskCreateDTO dto);
@Operation(
summary = "完成机器人节点, 继续流程任务"
)
@PostMapping("/api/process/task/robot/complete")
CommonResponse<Boolean> completeRobotTask(@Validated @RequestBody BpmnRobotTaskCompleteDTO dto);
@Operation(
summary = "根据实例 ID 和自然人 ID 查询对应待处理的任务 ID"
)
@GetMapping("/api/process/task/find")
CommonResponse<String> findTaskIdByInstanceIdAndPersonId(
@RequestParam(required = false) @NotBlank(message = "流程实例 ID 不能为空") String processInstanceId,
@RequestParam(required = false) @NotBlank(message = "自然人 ID 不能为空") String personId);
@Operation(
summary = "根据实例 ID列表 和自然人 ID 查询对应待处理的任务 ID"
)
@GetMapping("/api/process/task/batch/find")
CommonResponse<Map<String, String>> findTaskIdByInstanceIdsAndPersonId(
@RequestParam(required = false) @NotEmpty(message = "流程实例 ID列表 不能为空") List<String> processInstanceIds,
@RequestParam(required = false) @NotBlank(message = "自然人 ID 不能为空") String personId);
@Operation(
summary = "查询所有的审批流"
)
@PostMapping("/api/process/instance/page/all")
CommonResponse<BpmPageResult<BpmnProcessInstanceAdminPageItemVO>> getAllProcessInstancePage(
@Validated @RequestBody BpmnProcessInstanceAdminPageReqVO dto);
@Operation(
summary = "我发起的审批列表"
)
@PostMapping("/api/process/instance/page/my")
CommonResponse<BpmPageResult<BpmnProcessInstancePageItemVO>> getMyProcessInstancePage(
@Validated @RequestBody BpmnProcessInstanceMyPageReqVO dto);
@Operation(
summary = "创建审批流程, MQ 触发规则:1. 当前流程实例会依次触发 process-instance-created 和 process-instance-started 事件,2. 第一个审批任务会依次触发 process-task-assigned 和 process-task-created 事件"
)
@PostMapping("/api/process/instance/create")
CommonResponse<String> createProcessInstance(
@Validated @RequestBody BpmnProcessInstanceCreateDTO dto);
@Operation(
summary = "创建审批流程并带上表单"
)
@PostMapping("/api/process/instance/form/create")
CommonResponse<String> createProcessInstanceWith(
@Validated @RequestBody BpmnProcessInstanceCreateWithFormDTO dto);
@Operation(
summary = "发起人主动撤回审核,MQ 触发规则:1. 当前流程实例中现存的任务会依次触发 process-task-deleted 事件,2. 当前流程实例会触发 process-instance-cancelled 事件"
)
@DeleteMapping("/api/process/instance/cancel")
CommonResponse<Boolean> cancelProcessInstance(
@Validated @RequestBody BpmnProcessInstanceCancelDTO dto);
@Operation(
summary = "中止流程实例"
)
@DeleteMapping("/api/process/instance/abort")
CommonResponse<Boolean> abortProcessInstance(
@Validated @RequestBody BpmnProcessInstanceAbortDTO dto);
@Operation(
summary = "批量中止流程实例"
)
@DeleteMapping("/api/process/instance/batch/abort")
CommonResponse<BatchOperationResultVO> batchAbortProcessInstance(
@Validated @RequestBody List<BpmnProcessInstanceAbortDTO> dtos);
@Operation(
summary = "抄送流程实例"
)
@PostMapping("/api/process/instance/carbon-copy")
CommonResponse<Boolean> carbonCopyProcessInstance(
@Validated @RequestBody BpmnProcessInstanceCarbonCopyDTO dto);
@Operation(
summary = "获得流程实例"
)
@GetMapping("/api/process/instance/get")
CommonResponse<BpmnProcessInstanceVO> getProcessInstanceVO(
@Validated @RequestBody BpmnProcessInstanceQueryDTO dto);
@Operation(
summary = "更新指定流程定义的版本的状态, 处于 suspended 状态的流程模型将不能再发起实例"
)
@PutMapping("/api/process/instance/status/update")
CommonResponse<Boolean> updateProcessStatus(
@NotBlank(message = "流程定义 ID 不能为空") @RequestParam String processDefinitionId,
@NotNull(message = "状态不能为空") @RequestParam Integer status);
@Operation(
summary = "获取审批流程实例的运行图"
)
@GetMapping("/api/process/instance/graphical")
CommonResponse<ObjectNode> processInstanceGraphical(
@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@Operation(
summary = "推断指定流程实例的所有节点执行顺序"
)
@GetMapping("/api/process/instance/node/forecasting")
CommonResponse<List<ProcessNodeDetailVO>> processInstanceNodeForecast(
@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@Operation(
summary = "推断指定流程实例的过滤掉部分节点执行顺序"
)
@GetMapping("/api/process/instance/node/filter/forecasting")
CommonResponse<List<ProcessNodeDetailVO>> processInstanceFilterNodeForecast(
@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId,
@RequestParam(required = false, defaultValue = "false") Boolean allNode,
@Nullable @RequestParam(required = false) List<String> nodeDefinitionKeys);
@Operation(
summary = "获取指定流程实例的流程变量"
)
@GetMapping("/api/process/instance/cooperation-org")
CommonResponse<Map<String, Object>> getProcessVariables(
@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId,
@Nullable @RequestParam(required = false) String tenantId);
@Operation(
summary = "查询实例的租户集合"
)
@GetMapping("/api/process/instance/tenant/ids")
CommonResponse<List<String>> getTenantIds();
@Operation(
summary = "校验指定流程实例下,是否存在指定的审批人"
)
@PostMapping("/api/process/instance/check/approver")
CommonResponse<Boolean> checkInstanceApprover(
@Validated @RequestBody BpmnProcessInstanceCheckApproverDTO dto);
}

View File

@ -0,0 +1,26 @@
package cn.axzo.workflow.starter.api;
import cn.axzo.workflow.common.annotation.Manageable;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
/**
* TODO
*
* @author wangli
* @since 2024/6/11 16:32
*/
public interface WorkflowManageService {
/**
* 查询实例的租户集合
*
* @return
*/
@Operation(summary = "查询实例的租户集合")
@GetMapping("/api/process/instance/tenant/ids")
@Manageable
List<String> getTenantIds();
}

View File

@ -0,0 +1,25 @@
package cn.axzo.workflow.starter.feign.ext;
import feign.Feign;
import feign.Target;
/**
* TODO
*
* @author wangli
* @since 2024/6/11 15:40
*/
public class WorkflowEngineStarterFeign {
public static WorkflowEngineStarterFeign.Builder builder() {
return new WorkflowEngineStarterFeign.Builder();
}
public static final class Builder extends Feign.Builder {
@Override
public <T> T target(Target<T> target) {
return super.build().newInstance(target);
}
}
}

View File

@ -6,6 +6,7 @@ import cn.axzo.workflow.common.util.ThreadUtil;
import cn.axzo.workflow.starter.WorkflowEngineStarterProperties;
import cn.azxo.framework.common.constatns.Constants;
import feign.Client;
import feign.Feign;
import feign.RequestInterceptor;
import feign.Retryer;
import feign.Target;
@ -17,6 +18,7 @@ import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignBuilderCustomizer;
import org.springframework.cloud.openfeign.Targeter;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
@ -45,6 +47,16 @@ public class WorkflowEngineStarterFeignConfiguration {
return new Retryer.Default(100, SECONDS.toMillis(1), 3);
}
@Bean
public Feign.Builder workflowEngineStarterFeignBuilder() {
return WorkflowEngineStarterFeign.builder();
}
@Bean
public Targeter workflowEngineStarterFeignTargeter(WorkflowEngineStarterProperties starterProperties) {
return new WorkflowEngineStarterTargeter(starterProperties);
}
@Bean
public Client complexInvokeClient(WorkflowEngineStarterProperties starterProperties,
@Qualifier("workflowEngineStarterEventProducer") EventProducer eventProducer,

View File

@ -0,0 +1,27 @@
package cn.axzo.workflow.starter.feign.ext;
import feign.Target;
/**
* TODO
*
* @author wangli
* @since 2024/6/11 17:00
*/
public class WorkflowEngineStarterTarget<T> extends Target.HardCodedTarget<T> {
private final Class<?> manageableInterface;
public WorkflowEngineStarterTarget(Class<T> type, String url, Class<?> manageableInterface) {
super(type, url);
this.manageableInterface = manageableInterface;
}
public WorkflowEngineStarterTarget(Class<T> type, String name, String url, Class<?> manageableInterface) {
super(type, name, url);
this.manageableInterface = manageableInterface;
}
public Class<?> getManageableInterface() {
return manageableInterface;
}
}

View File

@ -0,0 +1,36 @@
package cn.axzo.workflow.starter.feign.ext;
import cn.axzo.workflow.starter.WorkflowEngineStarterProperties;
import cn.axzo.workflow.starter.api.WorkflowManageService;
import feign.Feign;
import feign.Target;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.Targeter;
/**
* TODO
*
* @author wangli
* @since 2024/6/11 16:12
*/
public class WorkflowEngineStarterTargeter implements Targeter {
private final WorkflowEngineStarterProperties properties;
public WorkflowEngineStarterTargeter(WorkflowEngineStarterProperties starterProperties) {
this.properties = starterProperties;
}
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) {
if (!(feign instanceof WorkflowEngineStarterFeign.Builder)) {
return feign.target(target);
}
Class<?> clazz = null;
if (properties.getManageable()) {
clazz = WorkflowManageService.class;
}
Target<T> newTarget = new WorkflowEngineStarterTarget<>(target.type(), target.name(), target.url(), clazz);
return feign.target(newTarget);
}
}

View File

@ -0,0 +1,415 @@
/**
* Copyright 2012-2020 The Feign Authors
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package feign;
import cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterTarget;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.Param.Expander;
import feign.Request.Options;
import feign.codec.Decoder;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;
import feign.template.UriUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import static feign.Util.checkArgument;
import static feign.Util.checkNotNull;
public class ReflectiveFeign extends Feign {
private final ParseHandlersByName targetToHandlersByName;
private final InvocationHandlerFactory factory;
private final QueryMapEncoder queryMapEncoder;
ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
QueryMapEncoder queryMapEncoder) {
this.targetToHandlersByName = targetToHandlersByName;
this.factory = factory;
this.queryMapEncoder = queryMapEncoder;
}
/**
* creates an api binding to the {@code target}. As this invokes reflection, care should be taken
* to cache the result.
*/
@SuppressWarnings("unchecked")
@Override
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
List<Class<?>> classList = new ArrayList<>();
classList.add(target.type());
if (target instanceof WorkflowEngineStarterTarget) {
Class<?> manageableInterface = ((WorkflowEngineStarterTarget<T>) target).getManageableInterface();
if (Objects.nonNull(manageableInterface)) {
classList.add(manageableInterface);
}
}
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
classList.toArray(new Class<?>[0]), handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof FeignInvocationHandler) {
FeignInvocationHandler other = (FeignInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
}
static final class ParseHandlersByName {
private final Contract contract;
private final Options options;
private final Encoder encoder;
private final Decoder decoder;
private final ErrorDecoder errorDecoder;
private final QueryMapEncoder queryMapEncoder;
private final SynchronousMethodHandler.Factory factory;
ParseHandlersByName(
Contract contract,
Options options,
Encoder encoder,
Decoder decoder,
QueryMapEncoder queryMapEncoder,
ErrorDecoder errorDecoder,
SynchronousMethodHandler.Factory factory) {
this.contract = contract;
this.options = options;
this.factory = factory;
this.errorDecoder = errorDecoder;
this.queryMapEncoder = queryMapEncoder;
this.encoder = checkNotNull(encoder, "encoder");
this.decoder = checkNotNull(decoder, "decoder");
}
public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate =
new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
}
if (md.isIgnored()) {
result.put(md.configKey(), args -> {
throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
});
} else {
result.put(md.configKey(),
factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
}
return result;
}
}
private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory {
private final QueryMapEncoder queryMapEncoder;
protected final MethodMetadata metadata;
protected final Target<?> target;
private final Map<Integer, Expander> indexToExpander = new LinkedHashMap<Integer, Expander>();
private BuildTemplateByResolvingArgs(MethodMetadata metadata, QueryMapEncoder queryMapEncoder,
Target target) {
this.metadata = metadata;
this.target = target;
this.queryMapEncoder = queryMapEncoder;
if (metadata.indexToExpander() != null) {
indexToExpander.putAll(metadata.indexToExpander());
return;
}
if (metadata.indexToExpanderClass().isEmpty()) {
return;
}
for (Entry<Integer, Class<? extends Expander>> indexToExpanderClass : metadata
.indexToExpanderClass().entrySet()) {
try {
indexToExpander
.put(indexToExpanderClass.getKey(), indexToExpanderClass.getValue().newInstance());
} catch (InstantiationException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
@Override
public RequestTemplate create(Object[] argv) {
RequestTemplate mutable = RequestTemplate.from(metadata.template());
mutable.feignTarget(target);
if (metadata.urlIndex() != null) {
int urlIndex = metadata.urlIndex();
checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex);
mutable.target(String.valueOf(argv[urlIndex]));
}
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
Object value = argv[entry.getKey()];
if (value != null) { // Null values are skipped.
if (indexToExpander.containsKey(i)) {
value = expandElements(indexToExpander.get(i), value);
}
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
}
RequestTemplate template = resolve(argv, mutable, varBuilder);
if (metadata.queryMapIndex() != null) {
// add query map parameters after initial resolve so that they take
// precedence over any predefined values
Object value = argv[metadata.queryMapIndex()];
Map<String, Object> queryMap = toQueryMap(value);
template = addQueryMapQueryParameters(queryMap, template);
}
if (metadata.headerMapIndex() != null) {
template =
addHeaderMapHeaders((Map<String, Object>) argv[metadata.headerMapIndex()], template);
}
return template;
}
private Map<String, Object> toQueryMap(Object value) {
if (value instanceof Map) {
return (Map<String, Object>) value;
}
try {
return queryMapEncoder.encode(value);
} catch (EncodeException e) {
throw new IllegalStateException(e);
}
}
private Object expandElements(Expander expander, Object value) {
if (value instanceof Iterable) {
return expandIterable(expander, (Iterable) value);
}
return expander.expand(value);
}
private List<String> expandIterable(Expander expander, Iterable value) {
List<String> values = new ArrayList<String>();
for (Object element : value) {
if (element != null) {
values.add(expander.expand(element));
}
}
return values;
}
@SuppressWarnings("unchecked")
private RequestTemplate addHeaderMapHeaders(Map<String, Object> headerMap,
RequestTemplate mutable) {
for (Entry<String, Object> currEntry : headerMap.entrySet()) {
Collection<String> values = new ArrayList<String>();
Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null : nextObject.toString());
}
} else {
values.add(currValue == null ? null : currValue.toString());
}
mutable.header(currEntry.getKey(), values);
}
return mutable;
}
@SuppressWarnings("unchecked")
private RequestTemplate addQueryMapQueryParameters(Map<String, Object> queryMap,
RequestTemplate mutable) {
for (Entry<String, Object> currEntry : queryMap.entrySet()) {
Collection<String> values = new ArrayList<String>();
boolean encoded = metadata.queryMapEncoded();
Object currValue = currEntry.getValue();
if (currValue instanceof Iterable<?>) {
Iterator<?> iter = ((Iterable<?>) currValue).iterator();
while (iter.hasNext()) {
Object nextObject = iter.next();
values.add(nextObject == null ? null
: encoded ? nextObject.toString()
: UriUtils.encode(nextObject.toString()));
}
} else if (currValue instanceof Object[]) {
for (Object value : (Object[]) currValue) {
values.add(value == null ? null
: encoded ? value.toString() : UriUtils.encode(value.toString()));
}
} else {
values.add(currValue == null ? null
: encoded ? currValue.toString() : UriUtils.encode(currValue.toString()));
}
mutable.query(encoded ? currEntry.getKey() : UriUtils.encode(currEntry.getKey()), values);
}
return mutable;
}
protected RequestTemplate resolve(Object[] argv,
RequestTemplate mutable,
Map<String, Object> variables) {
return mutable.resolve(variables);
}
}
private static class BuildFormEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs {
private final Encoder encoder;
private BuildFormEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder,
QueryMapEncoder queryMapEncoder, Target target) {
super(metadata, queryMapEncoder, target);
this.encoder = encoder;
}
@Override
protected RequestTemplate resolve(Object[] argv,
RequestTemplate mutable,
Map<String, Object> variables) {
Map<String, Object> formVariables = new LinkedHashMap<String, Object>();
for (Entry<String, Object> entry : variables.entrySet()) {
if (metadata.formParams().contains(entry.getKey())) {
formVariables.put(entry.getKey(), entry.getValue());
}
}
try {
encoder.encode(formVariables, Encoder.MAP_STRING_WILDCARD, mutable);
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
return super.resolve(argv, mutable, variables);
}
}
private static class BuildEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs {
private final Encoder encoder;
private BuildEncodedTemplateFromArgs(MethodMetadata metadata, Encoder encoder,
QueryMapEncoder queryMapEncoder, Target target) {
super(metadata, queryMapEncoder, target);
this.encoder = encoder;
}
@Override
protected RequestTemplate resolve(Object[] argv,
RequestTemplate mutable,
Map<String, Object> variables) {
Object body = argv[metadata.bodyIndex()];
checkArgument(body != null, "Body parameter %s was null", metadata.bodyIndex());
try {
encoder.encode(body, metadata.bodyType(), mutable);
} catch (EncodeException e) {
throw e;
} catch (RuntimeException e) {
throw new EncodeException(e.getMessage(), e);
}
return super.resolve(argv, mutable, variables);
}
}
}

View File

@ -1,78 +0,0 @@
package cn.axzo.workflow.support.api;
import cn.axzo.workflow.client.config.WorkflowEngineClientAutoConfiguration;
import cn.axzo.workflow.common.annotation.InvokeMode;
import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCreateDTO;
import cn.axzo.workflow.starter.WorkflowEngineStarterAutoConfiguration;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.expr.NormalAnnotationExpr;
import com.github.javaparser.printer.DefaultPrettyPrinter;
import com.github.javaparser.printer.Printer;
import com.github.javaparser.utils.CodeGenerationUtils;
import com.github.javaparser.utils.SourceRoot;
import lombok.SneakyThrows;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
/**
* TODO
*
* @author wangli
* @since 2024/6/7 17:14
*/
public class CodeGeneration {
@SneakyThrows
public static void main(String[] args) {
Path sourceCodeRoot = CodeGenerationUtils.mavenModuleRoot(WorkflowEngineClientAutoConfiguration.class).resolve("src/main/java/cn/axzo/workflow/client/feign/");
SourceRoot sourceRoot = new SourceRoot(sourceCodeRoot);
sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
Printer printer = new DefaultPrettyPrinter();
sourceRoot.setPrinter(printer::print);
List<ParseResult<CompilationUnit>> parseResults = sourceRoot.tryToParse();
List<CompilationUnit> compilationUnits = sourceRoot.getCompilationUnits();
compilationUnits.forEach(compilationUnit -> {
// System.out.println("compilationUnit = " + compilationUnit);
});
// https://www.jianshu.com/p/04b413c97988
Path outputCodeRoot = CodeGenerationUtils.mavenModuleRoot(WorkflowEngineStarterAutoConfiguration.class).resolve(Paths.get("src/main/java/cn/axzo/workflow/starter/api"));
SourceRoot outputCodeSR = new SourceRoot(outputCodeRoot);
outputCodeSR.saveAll(StandardCharsets.UTF_8);
CompilationUnit compilationUnit = new CompilationUnit();
ClassOrInterfaceDeclaration workflowCoreServiceTest = compilationUnit.addInterface("WorkflowCoreServiceTest").setPublic(true);
workflowCoreServiceTest.setInterface(true);
MethodDeclaration a = workflowCoreServiceTest.addMethod("a", Modifier.Keyword.PUBLIC).removeBody();
a.setJavadocComment("创建审批流程");
Parameter dto = a.addAndGetParameter(BpmnProcessInstanceCreateDTO.class, "dto");
dto.addAnnotation(Validated.class).addAnnotation(RequestBody.class);
a.setType(String.class);
NormalAnnotationExpr postMapping = a.addAndGetAnnotation(PostMapping.class);
postMapping.addPair("value", "/api/process/instance/create");
// postMapping.setName("/api/process/instance/create");
NormalAnnotationExpr invokeMode = a.addAndGetAnnotation(InvokeMode.class);
invokeMode.addPair("value", "SYNC");
// compilationUnit.setPackageDeclaration("cn.axzo.workflow.starter.api");
compilationUnit.addImport(BpmnProcessInstanceCreateDTO.class);
String string = workflowCoreServiceTest.toString();
System.out.println("string = " + string);
}
}

View File

@ -0,0 +1,190 @@
package cn.axzo.workflow.support.api;
import cn.axzo.workflow.client.config.WorkflowEngineClientAutoConfiguration;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.starter.WorkflowEngineStarterAutoConfiguration;
import cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.printer.DefaultPrettyPrinter;
import com.github.javaparser.printer.Printer;
import com.github.javaparser.utils.CodeGenerationUtils;
import com.github.javaparser.utils.SourceRoot;
import lombok.SneakyThrows;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
/**
* 生成 WorkflowCoreService 接口
*
* @author wangli
* @since 2024/6/7 17:14
*/
public class CoreServiceCodeGenerator {
@SneakyThrows
public static void main(String[] args) {
String newClassName = "WorkflowCoreService";
CompilationUnit originFile = parseOriginFile(newClassName);
CompilationUnit testGeneric = genericCode("cn.axzo.workflow.starter.api", newClassName);
writeToStarter(testGeneric, newClassName);
}
@SneakyThrows
private static CompilationUnit parseOriginFile(String newClassName) {
Path sourceCodeRoot = CodeGenerationUtils.mavenModuleRoot(WorkflowEngineStarterAutoConfiguration.class).resolve("src/main/java/cn/axzo/workflow/starter/api/");
SourceRoot sourceRoot = new SourceRoot(sourceCodeRoot);
sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
Printer printer = new DefaultPrettyPrinter();
sourceRoot.setPrinter(printer::print);
sourceRoot.tryToParse();
List<CompilationUnit> compilationUnits = sourceRoot.getCompilationUnits();
for (CompilationUnit cu : compilationUnits) {
Optional<String> primaryTypeName = cu.getPrimaryTypeName();
if (primaryTypeName.isPresent() && primaryTypeName.get().equals(newClassName)) {
System.out.println("compilationUnit = " + cu);
return cu;
}
}
return null;
}
private static void writeToStarter(CompilationUnit cu, String newClassName) {
String projectRootDir = System.getProperty("user.dir");
String genericFilePath = Paths.get(projectRootDir, "/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/", newClassName + ".java").toString();
try (FileWriter fileWriter = new FileWriter(genericFilePath)) {
fileWriter.append(cu.toString());
} catch (IOException e) {
// nothing to do
}
}
public static CompilationUnit genericCode(String pkg, String newClassName) {
CompilationUnit cu = new CompilationUnit(pkg);
ClassOrInterfaceDeclaration interfaceDeclaration = setCommon(cu, newClassName);
addMethods(interfaceDeclaration, cu);
addDefaultMethods(interfaceDeclaration);
return cu;
}
private static void addDefaultMethods(ClassOrInterfaceDeclaration interfaceDeclaration) {
MethodDeclaration sync = createDefaultMethod("sync", interfaceDeclaration);
sync.setJavadocComment("强制使用‘同步’模式调用该方法,请在调用真实方法前调用该方法\r\n" +
"<pre>\r\n" +
"workflowCoreService.sync().createProcessInstance();\r\n" +
"</pre>");
MethodDeclaration async = createDefaultMethod("async", interfaceDeclaration);
sync.setJavadocComment("强制使用‘异步’模式调用该方法,请在调用真实方法前调用该方法\r\n" +
"<pre>\r\n" +
"workflowCoreService.async().createProcessInstance();\r\n" +
"</pre>");
}
private static MethodDeclaration createDefaultMethod(String methodName, ClassOrInterfaceDeclaration interfaceDeclaration) {
// 将sync方法添加到WorkflowCoreService类中
MethodDeclaration methodDeclaration = interfaceDeclaration.addMethod(methodName, Modifier.Keyword.DEFAULT);
methodDeclaration.setType(new ClassOrInterfaceType("WorkflowCoreService"));
// 创建方法体
BlockStmt methodBody = new BlockStmt();
MethodCallExpr setMethodCall = new MethodCallExpr(new NameExpr("ThreadUtil"), "set");
setMethodCall.addArgument(new NameExpr(methodName.toUpperCase())); // 使用NameExpr来表示枚举常量
ExpressionStmt setStatement = new ExpressionStmt(setMethodCall);
methodBody.addStatement(setStatement);
// 创建return this;的语句
ReturnStmt returnStmt = new ReturnStmt(new ThisExpr());
methodBody.addStatement(returnStmt);
// 将方法体设置到sync方法中
methodDeclaration.setBody(methodBody);
return methodDeclaration;
}
@SneakyThrows
private static void addMethods(ClassOrInterfaceDeclaration interfaceDeclaration, CompilationUnit cu) {
Path sourceCodeRoot = CodeGenerationUtils.mavenModuleRoot(WorkflowEngineClientAutoConfiguration.class).resolve("src/main/java/cn/axzo/workflow/client/feign/");
SourceRoot sourceRoot = new SourceRoot(sourceCodeRoot);
sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
Printer printer = new DefaultPrettyPrinter();
sourceRoot.setPrinter(printer::print);
sourceRoot.tryToParse();
List<CompilationUnit> compilationUnits = sourceRoot.getCompilationUnits();
NodeList<BodyDeclaration<?>> targetMethods = interfaceDeclaration.getMembers();
for (CompilationUnit apiCU : compilationUnits) {
if (!apiCU.getPrimaryType().filter(e -> e.getAnnotationByClass(Manageable.class).isPresent()).isPresent()) {
addImports(apiCU, cu);
// 类上含有 @Manageable不进行解析
String interfaceName = apiCU.getPrimaryTypeName().get();
List<MethodDeclaration> methods = apiCU.getInterfaceByName(interfaceName).get().findAll(MethodDeclaration.class);
for (MethodDeclaration method : methods) {
if (!method.getAnnotationByClass(Manageable.class).isPresent()) {
MethodDeclaration methodDeclaration = method.clone();
methodDeclaration.setType(changeReturnType(methodDeclaration.getType()));
targetMethods.add(methodDeclaration);
}
}
}
}
}
private static Type changeReturnType(Type type) {
if (type instanceof ClassOrInterfaceType && type.asClassOrInterfaceType().getTypeArguments().isPresent()) {
NodeList<Type> types = type.asClassOrInterfaceType().getTypeArguments().orElse(new NodeList<>());
if (types.size() != 1) {
throw new RuntimeException("workflow-engine-api 中的接口返回类型有误");
}
return types.getFirst().get();
}
return type;
}
private static void addImports(CompilationUnit apiCU, CompilationUnit cu) {
for (ImportDeclaration sourceImport : apiCU.getImports()) {
cu.addImport(sourceImport.clone());
}
}
private static ClassOrInterfaceDeclaration setCommon(CompilationUnit cu, String newClassName) {
ClassOrInterfaceDeclaration classOrInterfaceDeclaration = cu.addInterface(newClassName).setPublic(true);
classOrInterfaceDeclaration.setJavadocComment("Workflow Engine Starter Core Service");
classOrInterfaceDeclaration.addAndGetAnnotation(FeignClient.class)
.addPair("name", new StringLiteralExpr("workflow-engine-starter"))
.addPair("url", new StringLiteralExpr("${axzo.service.workflow-engine:workflow-engine:8080}"))
.addPair("configuration", new ClassExpr(new ClassOrInterfaceType(null, WorkflowEngineStarterFeignConfiguration.class.getSimpleName())));
cu.addImport("cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration", false, false);
cu.addImport("cn.axzo.workflow.common.util.ThreadUtil", false, false);
cu.addImport("cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC", true, false);
cu.addImport("cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC", true, false);
return classOrInterfaceDeclaration;
}
}

View File

@ -0,0 +1,166 @@
package cn.axzo.workflow.support.api;
import cn.axzo.workflow.client.config.WorkflowEngineClientAutoConfiguration;
import cn.axzo.workflow.common.annotation.Manageable;
import cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.Modifier;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.ClassExpr;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.StringLiteralExpr;
import com.github.javaparser.ast.expr.ThisExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.ExpressionStmt;
import com.github.javaparser.ast.stmt.ReturnStmt;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.printer.DefaultPrettyPrinter;
import com.github.javaparser.printer.Printer;
import com.github.javaparser.utils.CodeGenerationUtils;
import com.github.javaparser.utils.SourceRoot;
import lombok.SneakyThrows;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
/**
* 生成 WorkflowManageService 接口
*
* @author wangli
* @since 2024/6/11 17:38
*/
public class ManageServiceCodeGenerator {
@SneakyThrows
public static void main(String[] args) {
String newClassName = "WorkflowManageService";
CompilationUnit testGeneric = genericCode("cn.axzo.workflow.starter.api", newClassName);
writeToStarter(testGeneric, newClassName);
}
private static void writeToStarter(CompilationUnit cu, String newClassName) {
String projectRootDir = System.getProperty("user.dir");
String genericFilePath = Paths.get(projectRootDir, "/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/", newClassName + ".java").toString();
try (FileWriter fileWriter = new FileWriter(genericFilePath)) {
fileWriter.append(cu.toString());
} catch (IOException e) {
// nothing to do
}
}
public static CompilationUnit genericCode(String pkg, String newClassName) {
CompilationUnit cu = new CompilationUnit(pkg);
ClassOrInterfaceDeclaration interfaceDeclaration = setCommon(cu, newClassName);
addMethods(interfaceDeclaration, cu);
addDefaultMethods(interfaceDeclaration);
return cu;
}
private static void addDefaultMethods(ClassOrInterfaceDeclaration interfaceDeclaration) {
MethodDeclaration sync = createDefaultMethod("sync", interfaceDeclaration);
sync.setJavadocComment("强制使用‘同步’模式调用该方法,请在调用真实方法前调用该方法\r\n" +
"<pre>\r\n" +
"workflowCoreService.sync().createProcessInstance();\r\n" +
"</pre>");
MethodDeclaration async = createDefaultMethod("async", interfaceDeclaration);
sync.setJavadocComment("强制使用‘异步’模式调用该方法,请在调用真实方法前调用该方法\r\n" +
"<pre>\r\n" +
"workflowCoreService.async().createProcessInstance();\r\n" +
"</pre>");
}
private static MethodDeclaration createDefaultMethod(String methodName, ClassOrInterfaceDeclaration interfaceDeclaration) {
// 将sync方法添加到WorkflowCoreService类中
MethodDeclaration methodDeclaration = interfaceDeclaration.addMethod(methodName, Modifier.Keyword.DEFAULT);
methodDeclaration.setType(new ClassOrInterfaceType("WorkflowCoreService"));
// 创建方法体
BlockStmt methodBody = new BlockStmt();
MethodCallExpr setMethodCall = new MethodCallExpr(new NameExpr("ThreadUtil"), "set");
setMethodCall.addArgument(new NameExpr(methodName.toUpperCase())); // 使用NameExpr来表示枚举常量
ExpressionStmt setStatement = new ExpressionStmt(setMethodCall);
methodBody.addStatement(setStatement);
// 创建return this;的语句
ReturnStmt returnStmt = new ReturnStmt(new ThisExpr());
methodBody.addStatement(returnStmt);
// 将方法体设置到sync方法中
methodDeclaration.setBody(methodBody);
return methodDeclaration;
}
@SneakyThrows
private static void addMethods(ClassOrInterfaceDeclaration interfaceDeclaration, CompilationUnit cu) {
Path sourceCodeRoot = CodeGenerationUtils.mavenModuleRoot(WorkflowEngineClientAutoConfiguration.class).resolve("src/main/java/cn/axzo/workflow/client/feign/");
SourceRoot sourceRoot = new SourceRoot(sourceCodeRoot);
sourceRoot.getParserConfiguration().setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8);
Printer printer = new DefaultPrettyPrinter();
sourceRoot.setPrinter(printer::print);
sourceRoot.tryToParse();
List<CompilationUnit> compilationUnits = sourceRoot.getCompilationUnits();
NodeList<BodyDeclaration<?>> targetMethods = interfaceDeclaration.getMembers();
for (CompilationUnit apiCU : compilationUnits) {
if (!apiCU.getPrimaryType().filter(e -> e.getAnnotationByClass(Manageable.class).isPresent()).isPresent()) {
addImports(apiCU, cu);
// 类上含有 @Manageable不进行解析
String interfaceName = apiCU.getPrimaryTypeName().get();
List<MethodDeclaration> methods = apiCU.getInterfaceByName(interfaceName).get().findAll(MethodDeclaration.class);
for (MethodDeclaration method : methods) {
if (!method.getAnnotationByClass(Manageable.class).isPresent()) {
MethodDeclaration methodDeclaration = method.clone();
methodDeclaration.setType(changeReturnType(methodDeclaration.getType()));
targetMethods.add(methodDeclaration);
}
}
}
}
}
private static Type changeReturnType(Type type) {
if (type instanceof ClassOrInterfaceType && type.asClassOrInterfaceType().getTypeArguments().isPresent()) {
NodeList<Type> types = type.asClassOrInterfaceType().getTypeArguments().orElse(new NodeList<>());
if (types.size() != 1) {
throw new RuntimeException("workflow-engine-api 中的接口返回类型有误");
}
return types.getFirst().get();
}
return type;
}
private static void addImports(CompilationUnit apiCU, CompilationUnit cu) {
for (ImportDeclaration sourceImport : apiCU.getImports()) {
cu.addImport(sourceImport.clone());
}
}
private static ClassOrInterfaceDeclaration setCommon(CompilationUnit cu, String newClassName) {
ClassOrInterfaceDeclaration classOrInterfaceDeclaration = cu.addInterface(newClassName).setPublic(true);
classOrInterfaceDeclaration.setJavadocComment("Workflow Engine Starter Management Service");
classOrInterfaceDeclaration.addAndGetAnnotation(FeignClient.class)
.addPair("name", new StringLiteralExpr("workflow-engine-starter"))
.addPair("url", new StringLiteralExpr("${axzo.service.workflow-engine:workflow-engine:8080}"))
.addPair("configuration", new ClassExpr(new ClassOrInterfaceType(null, WorkflowEngineStarterFeignConfiguration.class.getSimpleName())));
cu.addImport("cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration", false, false);
cu.addImport("cn.axzo.workflow.common.util.ThreadUtil", false, false);
cu.addImport("cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC", true, false);
cu.addImport("cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC", true, false);
return classOrInterfaceDeclaration;
}
}