diff --git a/.gitignore b/.gitignore index 8b86a6454..d217b08a8 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ application-local.yml *.log rebel.xml -.flattened-pom.xml \ No newline at end of file +.flattened-pom.xml +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7d0da22f8..e75deb2f5 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ Workflow Engine - 1.5.1-SNAPSHOT + 1.5.2-SNAPSHOT 2.0.0-SNAPSHOT 2.0.0-SNAPSHOT 1.0.0-SNAPSHOT @@ -30,6 +30,7 @@ 3.26.0 7.10.2 2.0.0 + 2.5.0 @@ -48,6 +49,12 @@ ${axzo-dependencies.version} pom import + + + com.xuxueli + xxl-job-core + + io.github.openfeign @@ -161,6 +168,16 @@ org-api ${axzo-dependencies.org.version} + + com.xuxueli + xxl-job-core + ${xxl-job.version} + + + cn.axzo.nanopart + doc-api + ${axzo-dependencies.version} + diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowEngineClientAutoConfiguration.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowEngineClientAutoConfiguration.java index a0a716cee..37beb9211 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowEngineClientAutoConfiguration.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowEngineClientAutoConfiguration.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; @@ -84,7 +83,7 @@ public class WorkflowEngineClientAutoConfiguration { Map env = new HashMap<>(); env.put("create", "true"); try { - FileSystem zipfs = FileSystems.newFileSystem(location.toURI(), env); + FileSystems.newFileSystem(location.toURI(), env); } catch (Exception e1) { log.error("linux env create new FS error: {}", e1.getMessage()); } @@ -102,7 +101,7 @@ public class WorkflowEngineClientAutoConfiguration { Map env = new HashMap<>(); env.put("create", "true"); try { - FileSystem zipfs = FileSystems.newFileSystem(location.toURI(), env); + FileSystems.newFileSystem(location.toURI(), env); } catch (Exception e) { log.error("linux env create new FS error: {}", e.getMessage()); } diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowRequestInterceptor.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowRequestInterceptor.java index a5c831343..cfdfd3958 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowRequestInterceptor.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/config/WorkflowRequestInterceptor.java @@ -24,6 +24,7 @@ public class WorkflowRequestInterceptor implements RequestInterceptor { public static final String HEADER_HTTP_CLIENT_VALUE = "WorkflowEngine-Feign"; public static final String HEADER_API_VERSION = "Service-Version"; public static final String HEADER_SERVER_NAME = "X-SERVER-NAME"; + public static final String HEADER_W_E = "WE"; @Override diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java index f7d41fcfc..1cbca027b 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessActivityApi.java @@ -3,7 +3,6 @@ package cn.axzo.workflow.client.feign.bpmn; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.Manageable; -import cn.axzo.workflow.common.enums.RpcInvokeModeEnum; import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO; import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO; diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java index 345f3083e..6b196947d 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessDefinitionApi.java @@ -1,7 +1,6 @@ package cn.axzo.workflow.client.feign.bpmn; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; -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.definition.BpmnProcessDefinitionUpdateDTO; @@ -10,7 +9,6 @@ import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessDefinitionP import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; import cn.azxo.framework.common.model.CommonResponse; -import org.springframework.cloud.openfeign.FeignClient; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -20,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestParam; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC; /** @@ -81,7 +80,8 @@ public interface ProcessDefinitionApi { */ @GetMapping("/api/process/definition/active/getByKey") @InvokeMode(SYNC) - CommonResponse getActiveProcessDefinitionByKey(@NotBlank(message = "模型定义KEY不能为空") @RequestParam String key); + CommonResponse getActiveProcessDefinitionByKey(@NotBlank(message = "模型定义KEY不能为空") @RequestParam String key, + @RequestParam(required = false, defaultValue = NO_TENANT_ID) String tenantId); /** diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java index e9548f543..57f4aed86 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessInstanceApi.java @@ -3,23 +3,34 @@ package cn.axzo.workflow.client.feign.bpmn; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.Manageable; +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO; 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.BpmnProcessInstanceLogQueryDTO; 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.process.SuperBpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; 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.BpmnProcessInstanceLogVO; 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.NodesByModelVO; import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; +import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO; +import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo; import cn.azxo.framework.common.model.CommonResponse; import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.Operation; @@ -48,6 +59,18 @@ import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC; //@FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = CommonFeignConfiguration.class) @WorkflowEngineFeignClient public interface ProcessInstanceApi { + + /** + * 创建流程前的节点列表 + * 用于发起人自选 + * + * @return + */ + @Operation(summary = "创建审批流程前,返回模型节点列表以及节点能否设置审批人") + @PostMapping("/api/process/instance/create/before") + @InvokeMode(SYNC) + CommonResponse> nodesBeforeCreateProcessInstance(@Validated @RequestBody BeforeProcessInstanceCreateDTO dto); + /** * 创建审批流程 * @@ -80,6 +103,10 @@ public interface ProcessInstanceApi { @DeleteMapping("/api/process/instance/cancel") CommonResponse cancelProcessInstance(@Validated @RequestBody BpmnProcessInstanceCancelDTO dto); + @DeleteMapping("/api/process/instance/super/cancel") + @Manageable + CommonResponse superCancelProcessInstance(@Validated @RequestBody SuperBpmnProcessInstanceCancelDTO dto); + /** * 中止流程实例 * @@ -222,7 +249,7 @@ public interface ProcessInstanceApi { CommonResponse> getTenantIds(); /** - * 校验指定流程实例下,是否存在指定的审批人 + * 校验指定流程实例下,是否存在指定的审批人正处理待审批 * * @return true 是在当前流程实例中,存在指定的审批人 */ @@ -242,4 +269,64 @@ public interface ProcessInstanceApi { @PostMapping("/api/process/instance/logs") @InvokeMode(SYNC) CommonResponse getProcessInstanceLogs(@Validated @RequestBody BpmnProcessInstanceLogQueryDTO dto); + + /** + * 根据任务id查询任务状态,按钮详情 + * + * @param taskButtonsSearchDTO 请求参数 + * @return + */ + @Operation(summary = "根据任务id查询任务状态,按钮详情") + @PostMapping("/api/process/instance/task/buttons/find") + @InvokeMode(SYNC) + CommonResponse findProcessSingleTaskButtons(@Validated @RequestBody BpmnTaskButtonSearchDTO taskButtonsSearchDTO); + + /** + * 更新指定流程表单最后一次编辑的内容 + * + * @param dto + * @return + */ + @Operation(summary = "更新指定流程表单最后一次编辑的内容") + @PostMapping("/api/process/instance/form/variable/update") + @InvokeMode(SYNC) + CommonResponse updateInstanceFormVariables(@Validated @RequestBody FormVariablesUpdateDTO dto); + + /** + * 签署业务流程实例在审批待办中查询使用的文档列表 + * + * @return + */ + @Operation(summary = "签署业务流程实例在审批待办中查询使用的文档列表") + @PostMapping("/api/process/instance/select/doc/list") + @InvokeMode(SYNC) + CommonResponse> processInstanceSelectDocs(@Validated @RequestBody ProcessDocQueryDTO dto); + + /** + * 获取审批人阅读状态 + * + * @return + */ + @Operation(summary = "获取审批人阅读状态") + @PostMapping("/api/process/instance/approver/read/status") + @InvokeMode(SYNC) + CommonResponse> approverReadStatus(@Validated @RequestBody ApproverReadStatusDTO dto); + + /** + * 修改审批人关联文档阅读状态 + */ + @Operation(summary = "修改审批人关联文档阅读状态") + @PostMapping("/api/process/instance/approver/read/status/change") + @InvokeMode(SYNC) + CommonResponse approveReadStatusChange(@Validated @RequestBody ChangeApproverReadStatusDTO dto); + + /** + * 获取签署业务流程最后替换的文档 fileKey 集合 + * + * @return + */ + @Operation(summary = "获取签署业务流程最后替换的文档 fileKey 集合") + @GetMapping("/api/process/instance/final/docs") + @InvokeMode(SYNC) + CommonResponse> getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId); } diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java index 3999a95fb..02ad5f6ee 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessModelApi.java @@ -6,9 +6,23 @@ 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; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocByIdDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCreateDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocOrderDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocResetDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocTenantQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocUpdateDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.RestPrintTemplateConfigDTO; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelDetailVO; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import cn.axzo.workflow.common.model.response.print.PrintModelDTO; import cn.azxo.framework.common.model.CommonResponse; import io.swagger.v3.oas.annotations.Operation; import org.springframework.validation.annotation.Validated; @@ -168,8 +182,23 @@ public interface ProcessModelApi { @PostMapping("/api/process/model/changeStatus") @InvokeMode(SYNC) CommonResponse changeStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, - @NotNull(message = "状态不能为空") @RequestParam Integer status, - @RequestParam(required = false) String operator); + @NotNull(message = "状态不能为空") @RequestParam Integer status, + @RequestParam(required = false) String operator); + + /** + * 修改模型打印开关状态 + * + * @param modelId + * @param status + * @param operator + * @return + */ + @Operation(summary = "修改模型打印开关状态") + @PostMapping("/api/process/model/print/changeStatus") + @InvokeMode(SYNC) + CommonResponse changePrintStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, + @NotNull(message = "状态不能为空") @RequestParam Integer status, + @RequestParam(required = false) String operator); /** * 查询流程模型使用的分类列表 @@ -190,4 +219,174 @@ public interface ProcessModelApi { @GetMapping("/api/process/model/tenant/ids") @InvokeMode(SYNC) CommonResponse> getModelTenantIds(); + + /** + * 打印模板配置内容更新保存 + * + * @param dto + * @return + */ + @Operation(summary = "打印模板配置内容更新保存") + @PostMapping("/api/process/model/print/template/upsert") + @InvokeMode(SYNC) + CommonResponse printTemplateConfig(@Validated @RequestBody PrintTemplateConfigUpsertDTO dto); + + /** + * 获取打印模板配置内容 + * + * @param dto + * @return + */ + @Operation(summary = "获取打印模板配置内容") + @PostMapping("/api/process/model/print/template/config/query") + @InvokeMode(SYNC) + CommonResponse getPrintTemplateConfig(@Validated @RequestBody PrintTemplateConfigQueryDTO dto); + + /** + * 代运营充值的打印模板 + * + * @param dto + * @return + */ + @Operation(summary = "代运营重置打印模板") + @PostMapping(value = "/api/process/model/print/template/config/reset") + @InvokeMode(SYNC) + CommonResponse resetPrintTemplateConfig(@Validated @RequestBody RestPrintTemplateConfigDTO dto); + + /** + * 搜索文档列表 + * + * @param dto + * @return + */ + @Operation(summary = "搜索文档列表") + @PostMapping(value = "/api/process/model/doc/page") + @InvokeMode(SYNC) + CommonResponse> docPage(@Validated @RequestBody DocSearchDTO dto); + + /** + * 获取指定 docIds 文档列表 + * + * @return + */ + @Operation(summary = "获取指定 docIds 文档列表") + @PostMapping(value = "/api/process/model/doc/ids") + @InvokeMode(SYNC) + CommonResponse> docByIds(@Validated @RequestBody DocByIdDTO dto); + + /** + * 获取指定模板的原始文档列表 + * + * @param dto + * @return + */ + @Operation(summary = "根据业务 ID 获取模型文档列表,自动适配公共模板和代运营") + @PostMapping(value = "/api/process/model/doc/list") + @InvokeMode(SYNC) + CommonResponse> docList(@Validated @RequestBody DocQueryDTO dto); + + /** + * 获取关联 HiPrint 类型文档模板内容 + * + * @param fileRelationId + * @return + */ + @Operation(summary = "获取关联 HiPrint 类型文档模板内容") + @PostMapping(value = "/api/process/model/hi-print/content/get") + @InvokeMode(SYNC) + CommonResponse getHiPrintContent(@RequestParam String fileRelationId); + + /** + * 添加关联文档 + * + * @return + */ + @Operation(summary = "添加关联文档") + @PutMapping(value = "/api/process/model/doc/create") + @InvokeMode(SYNC) + CommonResponse createDoc(@Validated @RequestBody DocCreateDTO dto); + + /** + * 修改关联文档 + * + * @return + */ + @Operation(summary = "修改关联文档") + @PostMapping(value = "/api/process/model/doc/update") + @InvokeMode(SYNC) + CommonResponse updateDoc(@Validated @RequestBody DocUpdateDTO dto); + + /** + * 克隆关联文档 + * + * @param docId + * @return + */ + @Operation(summary = "克隆关联文档") + @PostMapping(value = "/api/process/model/doc/clone") + @InvokeMode(SYNC) + CommonResponse cloneDoc(@RequestParam("id") Long docId); + + /** + * 删除关联文档 + * + * @return + */ + @Operation(summary = "删除指定文档") + @DeleteMapping(value = "/api/process/model/doc/delete") + @InvokeMode(SYNC) + CommonResponse deleteDoc(@RequestParam("id") Long docId); + + /** + * 关联文档配置排序 + * + * @param dto + * @return + */ + @Operation(summary = "关联文档配置排序") + @PostMapping(value = "/api/process/model/doc/order") + @InvokeMode(SYNC) + CommonResponse orderDoc(@Validated @RequestBody DocOrderDTO dto); + + /** + * 重置关联文档 + * + * @param dto + * @return + */ + @Operation(summary = "重置关联文档配置") + @PostMapping(value = "/api/process/model/doc/reset") + @InvokeMode(SYNC) + CommonResponse resetDoc(@Validated @RequestBody DocResetDTO dto); + + /** + * 设置关联文档的停启用状态 + * + * @param dto + * @return + */ + @Operation(summary = "设置关联文档的停启用状态") + @PostMapping(value = "/api/process/model/doc/status") + @InvokeMode(SYNC) + CommonResponse statusDoc(@Validated @RequestBody DocStatusDTO dto); + + /** + * 设置关联文档的必选状态 + * + * @return + */ + @Operation(summary = "设置关联文档的必选状态") + @PostMapping(value = "/api/process/model/doc/require") + @InvokeMode(SYNC) + CommonResponse requireDoc(@Validated @RequestBody DocStatusDTO dto); + + /** + * 特殊的查询设置过关联过文档的工作台 ID 集合 + * + * @return + */ + @Operation(summary = "特殊的查询设置过关联过文档的工作台 ID 集合") + @PostMapping(value = "/api/process/model/has/docs/tenantId") + @InvokeMode(SYNC) + CommonResponse> hasFilesTenantIds(@Validated @RequestBody DocTenantQueryDTO dto); } diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java index 9460b2eb2..a01f507c1 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessTaskApi.java @@ -3,25 +3,10 @@ package cn.axzo.workflow.client.feign.bpmn; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.Manageable; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO; -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.BpmnTaskAuditWithFormDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO; -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.request.bpmn.task.*; 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.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.axzo.workflow.common.model.response.bpmn.task.*; import cn.azxo.framework.common.model.CommonResponse; import io.swagger.v3.oas.annotations.Operation; import org.springframework.validation.annotation.Validated; @@ -104,6 +89,15 @@ public interface ProcessTaskApi { @PostMapping("/api/process/task/back") CommonResponse backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto); + /** + * 用于系统内部操作,跳转到指定节点 + * @param dto 请求参数 + * @return 是否成功 + */ + @Operation(summary = "系统操作回退任务到指定节点") + @PostMapping("/api/process/task/system/back") + CommonResponse systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto); + /** * 驳回 * @@ -284,4 +278,5 @@ public interface ProcessTaskApi { @InvokeMode(SYNC) CommonResponse> findTaskIdByInstanceIdsAndPersonId(@RequestParam(required = false) @NotEmpty(message = "流程实例 ID列表 不能为空") List processInstanceIds, @RequestParam(required = false) @NotBlank(message = "自然人 ID 不能为空") String personId); + } diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java index 614216a7a..88ca06197 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/bpmn/ProcessVariableApi.java @@ -1,12 +1,10 @@ package cn.axzo.workflow.client.feign.bpmn; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; -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.RestBpmnProcessVariable; import cn.azxo.framework.common.model.CommonResponse; -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.PathVariable; diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/PrintAdminApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/PrintAdminApi.java new file mode 100644 index 000000000..3c3e6365f --- /dev/null +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/PrintAdminApi.java @@ -0,0 +1,45 @@ +package cn.axzo.workflow.client.feign.manage; + +import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; +import cn.axzo.workflow.common.annotation.InvokeMode; +import cn.axzo.workflow.common.annotation.Manageable; +import cn.axzo.workflow.common.model.dto.print.PrintFieldDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintFieldQueryDTO; +import cn.azxo.framework.common.model.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.validation.constraints.NotBlank; +import java.util.List; +import java.util.Map; + +import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC; + +/** + * 打印相关的 API + * + * @author wangli + * @since 2025-01-16 17:35 + */ +@WorkflowEngineFeignClient +@Manageable +public interface PrintAdminApi { + @Operation(summary = "查询指定审批流程是否能打印,打印开关是否开启,是否存在打印模板") + @GetMapping("/api/print/admin/template/exists") + @InvokeMode(SYNC) + CommonResponse hasPrintTemplate(@NotBlank(message = "流程实例不能为空") @RequestParam String processInstanceId); + + @Operation(summary = "获取打印模板中可打印的字段") + @PostMapping("/api/print/admin/fields") + @InvokeMode(SYNC) + CommonResponse> getPrintFields(@Validated @RequestBody PrintFieldQueryDTO dto); + + @Operation(summary = "获取指定流程下用于替换打印的相关变量") + @GetMapping("/api/print/admin/field/variables") + @InvokeMode(SYNC) + CommonResponse> getPrintFieldVariables(@NotBlank(message = "流程实例不能为空") @RequestParam String processInstanceId); +} diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java index 7dea9ce08..b65095a8e 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessCategoryApi.java @@ -1,31 +1,20 @@ package cn.axzo.workflow.client.feign.manage; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; -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.category.CategoryConfigCreateDTO; -import cn.axzo.workflow.common.model.request.category.CategoryConfigSearchDTO; -import cn.axzo.workflow.common.model.request.category.CategoryCreateDTO; -import cn.axzo.workflow.common.model.request.category.CategorySearchDTO; -import cn.axzo.workflow.common.model.request.category.CategoryUpdateDTO; +import cn.axzo.workflow.common.model.request.category.*; 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.CategoryGroupVarItemVo; import cn.axzo.workflow.common.model.response.category.CategoryItemVO; import cn.azxo.framework.common.model.CommonResponse; -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.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import java.util.List; -import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC; +import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.*; /** @@ -177,4 +166,21 @@ public interface ProcessCategoryApi { @GetMapping("/api/process/category/check/status") @InvokeMode(SYNC) CommonResponse checkCategoryStatus(@RequestParam Long tenantId, @RequestParam String categoryCode); + + /** + * 查询分类对应的分组以及分组下的变量 + * @param dto 请求参数 + * @return 分组以及分组下的变量 + */ + @PostMapping("/api/process/category/group-with-vars/list") + @InvokeMode(SYNC) + CommonResponse> searchCategoryGroupAndVars(@Validated @RequestBody CategoryGroupVarSearchDto dto); + + /** + * 新增或者更新分组或者变量 + * @param dto 请求参数 + * @return 是否成功 + */ + @PostMapping("/api/process/category/group-with-vars/upsert") + CommonResponse upsertCategoryGroupAndVars(@Validated @RequestBody CategoryGroupVarUpsertDto dto); } diff --git a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java index 13bf5e8e0..521386f0e 100644 --- a/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java +++ b/workflow-engine-api/src/main/java/cn/axzo/workflow/client/feign/manage/ProcessConfigApi.java @@ -1,12 +1,10 @@ package cn.axzo.workflow.client.feign.manage; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; -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.BpmnButtonMetaInfo; import cn.azxo.framework.common.model.CommonResponse; -import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; diff --git a/workflow-engine-axzo-ext/src/main/java/cn/axzo/workflow/admin/repository/entity/ExtAxProcessAdmin.java b/workflow-engine-axzo-ext/src/main/java/cn/axzo/workflow/admin/repository/entity/ExtAxProcessAdmin.java index 8e4bdd15c..d3f3332b9 100644 --- a/workflow-engine-axzo-ext/src/main/java/cn/axzo/workflow/admin/repository/entity/ExtAxProcessAdmin.java +++ b/workflow-engine-axzo-ext/src/main/java/cn/axzo/workflow/admin/repository/entity/ExtAxProcessAdmin.java @@ -4,7 +4,6 @@ import cn.axzo.framework.data.mybatisplus.model.BaseEntity; import cn.axzo.workflow.common.enums.AdminDataSource; import cn.axzo.workflow.common.enums.AdminRoleType; import cn.axzo.workflow.common.enums.AdminTypeEnum; -import cn.axzo.workflow.common.enums.WorkspaceType; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnInstanceRespCode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnInstanceRespCode.java index ce1bacf93..8d84ed9a8 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnInstanceRespCode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnInstanceRespCode.java @@ -26,8 +26,12 @@ public enum BpmnInstanceRespCode implements IModuleRespCode { PROCESS_INSTANCE_CANT_START("011", "流程实例不能启动"), TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS("012", "流程实例【{}】不存在, 不能评论"), RUNNING_INSTANCE_ONLY_FORECAST("013", "仅运行中的实例可以推测"), - ENGINE_EXEC_EXCEPTION("014", "引擎内部异常"), + ENGINE_EXEC_EXCEPTION("014", "引擎内部异常:【{}】"), PROCESS_TASK_NOT_EXISTS("015", "流程任务不存在或已处理"), + PROCESS_DOC_READ_PARAM_ERROR("016", "查询审批人阅读状态参数丢失自然人 ID 数据"), + PROCESS_DOC_ID_NOT_IN_MODEL("017", "当前流程中,不存在指定文档"), + PROCESS_SIGN_DATA_NOT_EXISTS("018", "签署业务审批未获取到初始模板复制数据"), + PROCESS_INSTANCE_CANT_REMIND("019", "流程实例【{}】不存在, 不能评论"), ; private final String code; private final String message; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnModelRespCode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnModelRespCode.java index 7ba846689..d1881847e 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnModelRespCode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnModelRespCode.java @@ -18,7 +18,16 @@ public enum BpmnModelRespCode implements IModuleRespCode { MODEL_KEY_NOT_EXISTS("003", "流程模型KEY【{}】不存在"), MODEL_NOT_EXISTS("004", "流程模型不存在"), BPMN_BYTES_NOT_EXISTS("005", "模型定义内容字节码不存在"), - MODEL_IS_DISABLE("006", "模型已经被停用") + MODEL_IS_DISABLE("006", "模型已经被停用"), + MODEL_FILE_TAG_DUPLICATE("007", "模型关联的文档业务标签重复"), + MODEL_FILE_TAG_EXISTS("008", "模型关联的文档业务标签已存在"), + MODEL_FILE_NOT_EXISTS("009", "模型关联的文档不存在或已被删除"), + MODEL_FILE_CLONE_ERROR("010", "克隆文档失败, 原因:【{}】"), + MODEL_FILE_TYPE_CLONE_NOT_SUPPORT("011", "不支持的文档类型克隆"), + MODEL_FILE_CONTENT_DATA_ERROR("012", "文档内容数据异常"), + MODEL_FILE_QUERY_ERROR("013", "文档搜索参数实例 ID 和业务 ID 不能同时为空"), + MODEL_FILE_HIPRINT_ID_INVAILD("014", "查询HiPrint文档主键参数非法"), + MODEL_FILE_REPLACE_CONTENT_ERROR("015", "替换文档变量失败,原因:【{}】"), ; private final String code; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnProcessDefinitionRespCode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnProcessDefinitionRespCode.java index 7824ba129..a7889dec2 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnProcessDefinitionRespCode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnProcessDefinitionRespCode.java @@ -22,7 +22,8 @@ public enum BpmnProcessDefinitionRespCode implements IModuleRespCode { PROCESS_DEFINITION_KEY_NOT_MATCH("007", "流程定义的标识不一致, 请修改 BPMN 流程图"), PROCESS_DEFINITION_NAME_NOT_MATCH("008", "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"), PROCESS_DEFINITION_HAS_DIRTY_DATA("009", "流程定义KEY【{}】存在脏数据,当前模型没有流程定义内容"), - PROCESS_DEFINITION_IS_INVALID("010", "暂时无法发起,请先配置流程模型") + PROCESS_DEFINITION_IS_INVALID("010", "暂时无法发起,请先配置流程模型"), + PROCESS_DEFINITION_ID_ILLEGAL("011", "流程定义 ID 数据不合法") ; private final String code; private final String message; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnTaskRespCode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnTaskRespCode.java index 7a57b5303..5ef7f0586 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnTaskRespCode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/BpmnTaskRespCode.java @@ -41,6 +41,7 @@ public enum BpmnTaskRespCode implements IModuleRespCode { BACK_NODE_CANNOT_REACHABLE("024", "退回节点【{}】不可达,不允许退回"), REACHED_BACKED_MAXIMUM_NUM("025", "达到回退操作次数上限【{}】次"), TRANSFER_TO_SELF("026", "任务不能转交给自己"), + REMIND_TASK_TOO_MANY("027", "催办任务数据异常") ; private final String code; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormInstanceRespCode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormInstanceRespCode.java index d77523cab..8b878fffe 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormInstanceRespCode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormInstanceRespCode.java @@ -16,6 +16,13 @@ public enum FormInstanceRespCode implements IModuleRespCode { FORM_PARAM_ERROR("001", "流程实例 ID 和任务 ID 不能都为空"), FORM_FIELD_NOT_FOUND("002", "无法获取全量表单字段权限配置信息"), FORM_FIELD_VALIDATOR_ERROR("003", "表单字段校验不通过"), + FORM_INSTANCE_PARSE_ERROR("004", "表单实例数据解析错误"), + FORM_INSTANCE_DATA_NOT_FOUND("005", "未找到指定实例【{}】的表单数据"), + FORM_DATA_PARSE_ERROR("006", "表单数据解析异常"), + FORM_DATA_PARSE_ERROR_BY_UPLOAD("007", "表单上传组件的数据解析异常"), + FORM_DATA_PARSE_ERROR_BY_IMAGE("008", "表单图片组件的数据解析异常"), + FORM_DATA_PARSE_ERROR_BY_CUSTOM_COMPONENT("009", "表单自定义组件的数据解析异常"), + FORM_DATA_PARSE_ERROR_BY_AMOUNT("010", "表单金额组件的数据解析异常"), ; private final String code; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormModelRespCode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormModelRespCode.java index 435901582..8742fc036 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormModelRespCode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/FormModelRespCode.java @@ -13,7 +13,7 @@ import lombok.Getter; @Getter @AllArgsConstructor public enum FormModelRespCode implements IModuleRespCode { - FORM_MODEL_NOT_EXISTS("001", "表单模型不存在"), + FORM_MODEL_NOT_EXISTS("001", "表单模型不存在,请检查审批模板的字段管理Tab页"), FORM_MODEL_ID_NOT_EXISTS("002", "表单模型ID【{}】不存在"), FORM_MODEL_EXISTS("003", "表单模型已存在"), ; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/OtherRespCode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/OtherRespCode.java index 3c58f9e0e..c8d4f93f3 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/OtherRespCode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/code/OtherRespCode.java @@ -19,7 +19,9 @@ public enum OtherRespCode implements IModuleRespCode { CLIENT_VERSION_SUPPORT("004", "客户端 JAR 包版本不支持,请升级到 {} 版本, 当前版本 {}"), MICRO_SERVER_NEED_REBUILD("005", "微服务 {} 需要重新编译发布, 如果是本地调用,请访问 WebApi 接口,在现有的接口地址前加上“web/v1/”即可"), MESSAGE_PUSH_EVENT_BUILD_ERROR("006", "不能使用 createEvent 函数创建`发送待办`的事件, 请调用 createPendingPushEvent 函数"), - ASYNC_JOB_EXECUTION_ERROR("007", "获取指定实例 ID【{}】的锁失败") + ASYNC_JOB_EXECUTION_ERROR("007", "获取指定实例 ID【{}】的锁失败"), + ILLEGAL_PARAM_ERROR("008", "非法的参数:【{}】"), + MESSAGE_IM_EVENT_BUILD_ERROR("009", "不能使用 createEvent 函数创建`IM 消息`的事件, 请调用 createIMEvent 函数"), ; private final String code; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java index c7d2b8406..b74f7a3f3 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java @@ -9,6 +9,7 @@ public interface BpmnConstants { /** * 引擎自己的隐藏指令 */ + String ADSCRIPTION_TENANT_ID = "adscriptionTenantId"; String FLOWABLE_SKIP_EXPRESSION_ENABLE = "[_FLOWABLE_SKIP_EXPRESSION_ENABLED_]"; String MQ_UNIQUE_ID = "[_MQ_UNIQUE_ID_]"; String PROCESS_OWNERSHIP_APPLICATION = "[_PROCESS_OWNERSHIP_APPLICATION_]"; @@ -33,6 +34,7 @@ public interface BpmnConstants { String INTERNAL_PROCESS_AGENT = "[_INTERNAL_PROCESS_AGENT]"; String CREATE_INSTANCE_PARAMS = "[_CREATE_INSTANCE_PARAMS]"; String INTERNAL_PROCESS_WORKSPACE_TYPE = "[_INTERNAL_PROCESS_WORKSPACE_TYPE]"; + String INTERNAL_PROCESS_BIZ_TYPE = "[_INTERNAL_PROCESS_BIZ_TYPE]"; // 用于多实例审批时,保存计算出来的审批人 String INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO = "[_ASSIGNEE_LIST_INFO_]"; // 单任务节点, @@ -41,6 +43,8 @@ public interface BpmnConstants { String OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT = "[_ASSIGNEE_INFO_SNAPSHOT_]"; String INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT = "[_ACTIVITY_INFO_SNAPSHOT_]"; String BIZ_NODE_ALTER = "[_BIZ_NODE_ALTER_]"; + String INITIATOR_SPECIFY = "[_INITIATOR_SPECIFY_]"; + String SIGNATURE_COLLECTION = "[_SIGNATURE_COLLECTION_]"; String PROCESS_PREFIX = "Flowable"; @Deprecated String OLD_TASK_ASSIGNEE_SKIP_FLAT = "taskSkip"; @@ -54,12 +58,14 @@ public interface BpmnConstants { String ROBOT_ASSIGNEE_ID = NO_TENANT_ID + "|" + ROBOT_ASSIGNEE + "|" + ROBOT_ASSIGNEE_TYPE; String HIDDEN_ASSIGNEE_ID = NO_TENANT_ID + "|hidden|" + TASK_ASSIGNEE_SKIP_FLAT; String NO_ASSIGNEE = "|"; + String FLOW_MODEL_BIZ_TYPE = "modelType"; String FLOW_NODE_JSON = "jsonValue"; String FLOW_SERVER_VERSION = "serverVersion"; String FLOW_SERVER_VERSION_121 = "1.2.1"; String FLOW_SERVER_VERSION_130 = "1.3.0"; // 1.4.2 开始启用新版本日志 String FLOW_SERVER_VERSION_142 = "1.4.2"; + String CONFIG_SIGN = "signConfig"; String CONFIG_NOTICE = "noticeConfig"; String CONFIG_APPROVE = "approveConfig"; String TEMPLATE_NOTICE_MESSAGE_CONFIG = "noticeMessageConfig"; @@ -71,6 +77,7 @@ public interface BpmnConstants { String TEMPLATE_NOTICE_MESSAGE_DESTINATION_POSITIONS = "positions"; String TEMPLATE_NOTICE_MESSAGE_DESTINATION_ASSIGNERS = "assigners"; String TEMPLATE_PENDING_MESSAGE_ID = "pendingMessageId"; + String TEMPLATE_SIGN_PENDING_MESSAGE_ID = "signPendingMessageId"; String TEMPLATE_CARBON_COPY_MESSAGE_ID = "carbonCopyMessageId"; String TEMPLATE_SMS_MESSAGE_ID = "smsMessageId"; String APPROVE_SUPPORT_BATCH_OPERATION = "supportBatchOperation"; @@ -84,10 +91,14 @@ public interface BpmnConstants { String CONFIG_BUTTON_META = "button"; String CONFIG_FIELD = "fieldConfig"; String CONFIG_APPROVAL_METHOD = "approvalMethod"; + String CONFIG_SIGN_APPROVER_LIMIT = "signApproverLimit"; + String CONFIG_SIGN_APPROVER_ORG_LIMIT = "orgLimit"; + String CONFIG_SIGN_APPROVER_ROLE_LIMIT = "roleLimit"; String CONFIG_APPROVER_SCOPE = "approverScope"; String CONFIG_APPROVER_SPECIFY = "approverSpecify"; String CONFIG_APPROVER_MODE_TYPE = "approverModeType"; String CONFIG_APPROVER_EMPTY_HANDLE_TYPE = "approverEmptyHandleType"; + String CONFIG_ACTIVITY_SIGNATURE = "signature"; String CONFIG_FIELD_META = "field"; String CONFIG_FIELD_PERMISSION = "fieldPermission"; String CONFIG_FIELD_OPTION = "option"; @@ -96,6 +107,7 @@ public interface BpmnConstants { String CONFIG_BUTTON_TYPE_CURRENT = "current"; String CONFIG_BUTTON_TYPE_HISTORY = "history"; String CONFIG_BUTTON_TYPE_CARBON_COPY = "carbonCopy"; + String CONFIG_SIGN_TYPE = "signType"; String ELEMENT_ATTRIBUTE_NAME = "name"; String ELEMENT_ATTRIBUTE_VALUE = "value"; String ELEMENT_ATTRIBUTE_DESC = "desc"; @@ -112,6 +124,7 @@ public interface BpmnConstants { String BPM_ALLOW_SKIP_USER_TASK = "_INTERNAL_SKIP_USER_TASK_"; String AUTO_APPROVAL_TYPE = "autoApprovalType"; String PROCESS_CLOSING_TYPE = "[_PROCESS_CLOSING_TYPE]"; + String SKIP_MQ = "skipMQ"; /** * 用于国内审批节点填写审批建议 *

@@ -199,4 +212,28 @@ public interface BpmnConstants { * ouId+workspaceId 下限制人员数量为20 */ Integer MAX_ORG_WORKSPACE_ADMIN_COUNT = 20; + + /** + * 发起人撤回时,当前流程停留住的节点定义key + */ + String CANCEL_PROCESS_NODE_DEF_KEY_NAME = "[_CANCEL_PROCESS_NODE_DEF_KEY_]"; + // 被转交人 + String TRANSFER_TO = "[TRANSFER_TO_]"; + // 转交时的意见 + String TRANSFER_TO_ADVICE = "[TRANSFER_TO_ADVICE]"; + // 结束流程的最后一个操作人 + String CLOSE_PROCESS_ASSIGNER = "[_CLOSE_PROCESS_ASSIGNER_]"; + /** + * 签署业务发起流程实例时,重新选择的文档tag 集合 + */ + String SIGN_PROCESS_ENABLE_DOC_IDS = "[_SIGN_PROCESS_ENABLE_DOC_IDS_]"; + /** + * 签署业务,基于业务自定义变量的传入 + */ + String SIGN_VARIABLE = "[_SIGN_VARIABLES_]"; + /** + * 签署人 + */ + String SIGNATORIES = "[_SIGNATORIES_]"; + String TASK_SUBMIT_FORM_VARIABLE = "[_TASK_SUBMIT_FORM_VARIABLE_]"; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/FormConstants.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/FormConstants.java index 2b4323bfc..5bbe9334b 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/FormConstants.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/FormConstants.java @@ -17,4 +17,13 @@ public interface FormConstants { String FIELD_PROPERTY_HIDDEN = "hidden"; String FIELD_PROPERTY_DEFAULT_VALUE= "defaultValue"; + + String FORM_FIELD_TYPE_INPUT = "input"; + String FORM_FIELD_TYPE_TEXTAREA = "textarea"; + String FORM_FIELD_TYPE_IMAGE = "image"; + String FORM_FIELD_TYPE_CUSTOM_COMPONENT = "customComponent"; + String FORM_FIELD_TYPE_TASK_ORDER = "taskOrder"; + String FORM_FIELD_TYPE_RECTIFY_ORDER = "rectifyOrder"; + String FORM_FIELD_TYPE_CHANGE_SIGNATURE_ORDER = "changeSignatureOrder"; + } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/TaskListenerExtConstants.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/TaskListenerExtConstants.java new file mode 100644 index 000000000..6e2d7a1ac --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/TaskListenerExtConstants.java @@ -0,0 +1,12 @@ +package cn.axzo.workflow.common.constant; + +/** + * 自定义的流程回退及转发事件常亮 + * + * @author wangli + * @since 2025-01-20 14:06 + */ +public interface TaskListenerExtConstants { + String EVENTNAME_TRANSFER = "process-task-transfer"; + String EVENTNAME_FALLBACK = "process-task-fallback"; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/VariableConstants.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/VariableConstants.java index e525d91f9..b0ae9caa8 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/VariableConstants.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/VariableConstants.java @@ -25,4 +25,40 @@ public interface VariableConstants { String VAR_PROCESS_RESULT = "processResult"; String VAR_OPERATOR_TYPE = "operatorType"; String VAR_USER_AGREE_SIGNATURE = "userAgreeSignature"; + + + //=============== 打印时的变量集合中 key 的命名 ================= + String VAR_PREFIX = "业务变量"; + String PRINT_VAR_PROCESS_DEFINITION_KEY = "processDefinitionKey"; + String PRINT_VAR_PROCESS_DEFINITION_KEY_DESC = "业务名称"; + String PRINT_VAR_PROCESS_INSTANCE_ID = "processInstanceId"; + String PRINT_VAR_PROCESS_INSTANCE_ID_DESC = "审批编号"; + String PRINT_VAR_PROCESS_START_TIME = "startTime"; + String PRINT_VAR_PROCESS_START_TIME_DESC = "发起时间"; + String PRINT_VAR_PROCESS_END_TIME = "endTime"; + String PRINT_VAR_PROCESS_END_TIME_DESC = "审批结束时间"; + String PRINT_VAR_PROCESS_INITIATOR = "initiator"; + String PRINT_VAR_PROCESS_INITIATOR_DESC = "发起者"; + String PRINT_VAR_PROCESS_INITIATOR_NAME = "initiatorName"; + String PRINT_VAR_PROCESS_INITIATOR_NAME_DESC = "发起人姓名"; + String PRINT_VAR_PROCESS_INITIATOR_POSITION = "initiatorPosition"; + String PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC = "发起人岗位"; + String PRINT_VAR_PROCESS_INITIATOR_PHONE = "initiatorPhone"; + String PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC = "发起人联系方式"; + String PRINT_VAR_PROCESS_LOGS = "processLogs"; + String PRINT_VAR_PROCESS_LOGS_DESC = "审批日志"; + String PRINT_VAR_PROCESS_LOG_ACTIVITY_NAME = "activityName"; + String PRINT_VAR_PROCESS_LOG_ACTIVITY_NAME_DESC = "节点名称"; + String PRINT_VAR_PROCESS_LOG_APPROVER_NAME = "approverName"; + String PRINT_VAR_PROCESS_LOG_APPROVER_NAME_DESC = "审批人"; + String PRINT_VAR_PROCESS_LOG_UNIT = "unit"; + String PRINT_VAR_PROCESS_LOG_UNIT_DESC = "单位"; + String PRINT_VAR_PROCESS_LOG_POSITION = "position"; + String PRINT_VAR_PROCESS_LOG_POSITION_DESC = "岗位"; + String PRINT_VAR_PROCESS_LOG_ADVICE = "advice"; + String PRINT_VAR_PROCESS_LOG_ADVICE_DESC = "审批意见"; + String PRINT_VAR_PROCESS_LOG_OPERATION_TIME = "operationTime"; + String PRINT_VAR_PROCESS_LOG_OPERATION_TIME_DESC = "审批时间"; + String PRINT_VAR_PROCESS_LOG_SIGNATURE = "signature"; + String PRINT_VAR_PROCESS_LOG_SIGNATURE_DESC = "电子签名"; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java index 2d6451b8e..5b8ab0b5e 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApprovalMethodEnum.java @@ -13,9 +13,12 @@ public enum ApprovalMethodEnum { human("human", "人工审批", ""), autoPassed("autoPassed", "自动通过", "[仅审批节点可能选该值]"), + autoPassed_empty("autoPassed", "自动通过", "该枚举仅日志处理使用"), autoRejection("autoRejection", "自动驳回", "[仅审批节点可能选该值]"), + autoRejection_empty("autoRejection", "自动驳回", "该枚举仅日志处理使用"), nobody("nobody", "不设置审批人", "[仅业务节点可能有该值]"), bizSpecify("bizSpecify", "业务指定审批人", "[仅业务节点可能有该值]"), + transferToAdmin("transferToAdmin", "转办给管理员", "该枚举仅日志处理使用"), ; private String type; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApproverSpecifyEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApproverSpecifyEnum.java index 85dbab935..8b18cd082 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApproverSpecifyEnum.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ApproverSpecifyEnum.java @@ -15,6 +15,9 @@ public enum ApproverSpecifyEnum { initiatorLeaderRecursion("initiatorLeaderRecursion", "发起人多级主管"), fixedPerson("fixedPerson", "固定人员"), preNodeSpecified("preNodeSpecified", "上级节点指定"), + initiatorSpecified("initiatorSpecified", "发起时指定"), + // 该枚举,目前主要为了区别签署业务与审批业务的不同配法,选择该项时,意味着该节点是由二方后端自由消费 BpmnSignApproverLimit 配置 + signerRelated("signerRelated", "签署人相关组织"), ; private String type; private String desc; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnButtonEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnButtonEnum.java index 1e391dc38..dae4550d9 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnButtonEnum.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnButtonEnum.java @@ -1,6 +1,13 @@ package cn.axzo.workflow.common.enums; +import com.google.common.collect.Lists; + +import java.util.List; + +import static cn.axzo.workflow.common.enums.BusinessTypeEnum.APPROVAL; +import static cn.axzo.workflow.common.enums.BusinessTypeEnum.SIGN; + /** * Flowable Event Enum For RocketMQ * @@ -11,39 +18,51 @@ public enum BpmnButtonEnum { /** * 同意按钮 */ - BPMN_APPROVE(1, "BPMN_APPROVE", "同意"), + BPMN_APPROVE(1, "BPMN_APPROVE", "同意", Lists.newArrayList(APPROVAL, SIGN)), /** * 驳回按钮 */ - BPMN_REJECT(2, "BPMN_REJECT", "驳回"), + BPMN_REJECT(2, "BPMN_REJECT", "驳回", Lists.newArrayList(APPROVAL, SIGN)), + /** + * 确认签署 + */ + BPMN_CONFIRM_SIGN(3, "BPMN_CONFIRM_SIGN", "确认签署", Lists.newArrayList(SIGN)), + /** + * 查看文档 + */ + BPMN_VIEW_DOC(4, "BPMN_VIEW_DOC", "查看文档", Lists.newArrayList(SIGN)), /** * 撤回按钮 */ - BPMN_REVOCATION(3, "BPMN_REVOCATION", "撤回"), + BPMN_REVOCATION(5, "BPMN_REVOCATION", "撤回", Lists.newArrayList(APPROVAL, SIGN)), /** * 转交按钮 */ - BPMN_TRANSFER(4, "BPMN_TRANSFER", "转交"), + BPMN_TRANSFER(6, "BPMN_TRANSFER", "转交", Lists.newArrayList(APPROVAL, SIGN)), /** * 加签按钮 */ - BPMN_COUNTERSIGN(5, "BPMN_COUNTERSIGN", "加签"), + BPMN_COUNTERSIGN(7, "BPMN_COUNTERSIGN", "加签", Lists.newArrayList(APPROVAL, SIGN)), /** * 评论按钮 */ - BPMN_COMMENT(6, "BPMN_COMMENT", "评论"), + BPMN_COMMENT(8, "BPMN_COMMENT", "评论", Lists.newArrayList(APPROVAL, SIGN)), /** * 回退按钮 */ - BPMN_ROLLBACK(7, "BPMN_ROLLBACK", "回退"), + BPMN_ROLLBACK(9, "BPMN_ROLLBACK", "回退", Lists.newArrayList(APPROVAL, SIGN)), /** * 抄送按钮 */ - BPMN_COPY(8, "BPMN_COPY", "抄送"), + BPMN_COPY(10, "BPMN_COPY", "抄送", Lists.newArrayList(APPROVAL, SIGN)), + /** + * 催办按钮 + */ + BPMN_REMIND(11, "BPMN_REMIND", "催办", Lists.newArrayList(APPROVAL, SIGN)), /** * 管理员转交按钮 */ - BPMN_ADMIN_TRANSFER(9, "BPMN_ADMIN_TRANSFER", "管理员转交"); + BPMN_ADMIN_TRANSFER(90, "BPMN_ADMIN_TRANSFER", "管理员转交", Lists.newArrayList()); public int getOrder() { @@ -58,15 +77,21 @@ public enum BpmnButtonEnum { return btnName; } + public List getSupportBizType() { + return supportBizType; + } + private final int order; private final String btnKey; private final String btnName; + private final List supportBizType; - BpmnButtonEnum(int order, String btnKey, String btnName) { + BpmnButtonEnum(int order, String btnKey, String btnName, List supportBizType) { this.order = order; this.btnKey = btnKey; this.btnName = btnName; + this.supportBizType = supportBizType; } } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java index 4f97157da..dfc36e971 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnFlowNodeType.java @@ -16,6 +16,7 @@ public enum BpmnFlowNodeType { NODE_PARALLEL("NODE_PARALLEL", "并行节点 - 隶属于并行网关"), NODE_TASK("NODE_TASK", "审核节点"), NODE_BUSINESS("NODE_BUSINESS", "业务节点"), + NODE_SIGN("NODE_SIGN", "签署确认节点"), NODE_CARBON_COPY("NODE_CARBON_COPY", "抄送节点"), NODE_TRIGGER("NODE_TRIGGER", "触发器节点"), NODE_ROBOT("NODE_ROBOT", "机器人节点"), diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java index 150ecbc35..eaa621a2e 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessInstanceResultEnum.java @@ -44,7 +44,7 @@ public enum BpmnProcessInstanceResultEnum { * @param result 结果 * @return 是否 */ - public static boolean isEndResult(Integer result) { + public static boolean isEndResult(String result) { return Arrays.asList(PROCESSING.getStatus(), APPROVED.getStatus(), REJECTED.getStatus(), CANCELLED.getStatus()).contains(result); } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessTaskResultEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessTaskResultEnum.java new file mode 100644 index 000000000..456dba759 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnProcessTaskResultEnum.java @@ -0,0 +1,29 @@ +package cn.axzo.workflow.common.enums; + +import lombok.Getter; + +@Getter +public enum BpmnProcessTaskResultEnum { + PENDING("PENDING", "待处理"), + APPROVED("APPROVED", "已同意"), + REJECTED("REJECTED", "已驳回"), + DELETED("DELETED", "已删除"), + CANCELED("CANCELED", "已撤销"), + TRANSFERRED("TRANSFERRED", "已转交"), + NONE("NONE", "没有执行动作,例如 抄送"), + ; + /** + * 结果 + */ + private final String status; + /** + * 描述 + */ + private final String desc; + + BpmnProcessTaskResultEnum(String status, String desc) { + this.status = status; + this.desc = desc; + } + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnSignType.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnSignType.java new file mode 100644 index 000000000..fe46981d7 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BpmnSignType.java @@ -0,0 +1,42 @@ +package cn.axzo.workflow.common.enums; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 签署方式 + * + * @author wangli + * @since 2025-03-25 17:00 + */ +public enum BpmnSignType { + + SINGLE("SINGLE", "指定人群,所有人共同签署一份文件"), + MULTI("MULTI", "指定人群,每人签署一份文件"), + ; + + private final String type; + private final String desc; + + BpmnSignType(String type, String desc) { + this.type = type; + this.desc = desc; + } + + public String getType() { + return type; + } + + + public String getDesc() { + return desc; + } + + public static BpmnSignType valueOfType(String type) { + return Arrays.stream(BpmnSignType.values()) + .filter(i -> Objects.equals(i.getType(), type)) + .findAny() + .orElse(null); + } + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BusinessTypeEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BusinessTypeEnum.java new file mode 100644 index 000000000..c44af52b2 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/BusinessTypeEnum.java @@ -0,0 +1,18 @@ +package cn.axzo.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +public enum BusinessTypeEnum { + + SIGN("sign", "签署业务"), + + APPROVAL("approval", "审批业务"); + + private String type; + private String desc; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ButtonVisibleScopeEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ButtonVisibleScopeEnum.java new file mode 100644 index 000000000..3f7dbdbbd --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ButtonVisibleScopeEnum.java @@ -0,0 +1,26 @@ +package cn.axzo.workflow.common.enums; + +import lombok.Getter; + +@Getter +public enum ButtonVisibleScopeEnum { + + INITIATOR("INITIATOR", "发起人"), + + EXECUTOR("EXECUTOR", "当前操作人"), + ; + + /** + * 结果 + */ + private final String status; + /** + * 描述 + */ + private final String desc; + + ButtonVisibleScopeEnum(String status, String desc) { + this.status = status; + this.desc = desc; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/DocChangeEventEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/DocChangeEventEnum.java new file mode 100644 index 000000000..c8eab6afe --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/DocChangeEventEnum.java @@ -0,0 +1,45 @@ +package cn.axzo.workflow.common.enums; + +import cn.axzo.framework.rocketmq.Event; + +/** + * 流程模型关联的文档变更 MQ 事件枚举定义 + * + * @author wangli + * @since 2025-04-07 16:46 + */ +public enum DocChangeEventEnum { + DOC_CHANGE("doc", "doc-change", "模型关联文档变更"), + ; + + private final String module; + private final String tag; + private final String desc; + private Event.EventCode eventCode; + + DocChangeEventEnum(String module, String tag, String desc) { + this.eventCode = Event.EventCode.builder() + .module(module) + .name(tag) + .build(); + this.module = module; + this.tag = tag; + this.desc = desc; + } + + public String getModule() { + return module; + } + + public String getTag() { + return tag; + } + + public String getDesc() { + return desc; + } + + public Event.EventCode getEventCode() { + return eventCode; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ExtModelStateFieldEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ExtModelStateFieldEnum.java new file mode 100644 index 000000000..f170a4a50 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ExtModelStateFieldEnum.java @@ -0,0 +1,11 @@ +package cn.axzo.workflow.common.enums; + +/** + * 模型扩展表的状态枚举 + * + * @author wangli + * @since 2025-01-15 09:46 + */ +public enum ExtModelStateFieldEnum { + status, printStatus +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/FileTypeEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/FileTypeEnum.java new file mode 100644 index 000000000..ae3c22c02 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/FileTypeEnum.java @@ -0,0 +1,46 @@ +package cn.axzo.workflow.common.enums; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 文档类型枚举 + * + * @author wangli + * @since 2025-03-27 09:55 + */ +public enum FileTypeEnum { + WORD("word", "文本", ".docx"), + EXCEL("excel", "表格", ".xlsx"), + HIPRINT("hiprint", "智能文档", ""), + PDF("pdf", "PDF", ".pdf"), + ; + private final String type; + private final String desc; + private final String suffix; + + FileTypeEnum(String type, String desc, String suffix) { + this.type = type; + this.desc = desc; + this.suffix = suffix; + } + + public String getType() { + return type; + } + + public String getDesc() { + return desc; + } + + public String getSuffix() { + return suffix; + } + + public static FileTypeEnum valueOfType(String type) { + return Arrays.stream(FileTypeEnum.values()) + .filter(i -> Objects.equals(i.getType().toUpperCase(), type.toUpperCase())) + .findAny() + .orElse(null); + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ModelBizTypeEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ModelBizTypeEnum.java new file mode 100644 index 000000000..a4d5c1bee --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ModelBizTypeEnum.java @@ -0,0 +1,39 @@ +package cn.axzo.workflow.common.enums; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 模型业务类型枚举 + * + * @author wangli + * @since 2025-03-26 11:49 + */ +public enum ModelBizTypeEnum { + + SIGN("SIGN", "签署业务"), + FLOWABLE("FLOWABLE", "审批业务"), + ; + private final String type; + private final String desc; + + ModelBizTypeEnum(String type, String desc) { + this.type = type; + this.desc = desc; + } + + public String getType() { + return type; + } + + public String getDesc() { + return desc; + } + + public static ModelBizTypeEnum valueOfType(String type) { + return Arrays.stream(ModelBizTypeEnum.values()) + .filter(i -> Objects.equals(i.getType(), type)) + .findAny() + .orElse(null); + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/OrderEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/OrderEnum.java new file mode 100644 index 000000000..805b79ded --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/OrderEnum.java @@ -0,0 +1,12 @@ +package cn.axzo.workflow.common.enums; + +/** + * 顺序操作枚举 + * + * @author wangli + * @since 2025-03-31 16:35 + */ +public enum OrderEnum { + + UP, DOWN +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/PrintFieldCategoryEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/PrintFieldCategoryEnum.java new file mode 100644 index 000000000..1a29b3e16 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/PrintFieldCategoryEnum.java @@ -0,0 +1,19 @@ +package cn.axzo.workflow.common.enums; + +/** + * 打印字段的类型枚举 + * + * @author wangli + * @since 2025-01-16 18:19 + */ +public enum PrintFieldCategoryEnum { + + // 表单变量 + form, + // 流程内系统变量 + system, + // 电子签名变量 + signature, + // 签署业务自定义变量 + sign +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessMessagePushEventEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessMessagePushEventEnum.java index f8885dadd..6d3300170 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessMessagePushEventEnum.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessMessagePushEventEnum.java @@ -17,6 +17,7 @@ public enum ProcessMessagePushEventEnum { PROCESS_CARBON_COPY("process-push", "process-carbon-copy", "抄送流程"), PROCESS_CARBON_COPY_COMPLETE("process-push", "process-carbon-copy-complete", "完成抄送"), PROCESS_PUSH_SMS("process-push", "process-push-sms", "短信推送"), + PROCESS_PUSH_IM("process-push", "process-push-im", "IM 推送"), ; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java index cfe9b8531..cd205223c 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/ProcessTaskEventEnum.java @@ -2,6 +2,9 @@ package cn.axzo.workflow.common.enums; import cn.axzo.framework.rocketmq.Event; +import static cn.axzo.workflow.common.constant.TaskListenerExtConstants.EVENTNAME_FALLBACK; +import static cn.axzo.workflow.common.constant.TaskListenerExtConstants.EVENTNAME_TRANSFER; + /** * 流程任务节点相关的 MQ 事件枚举定义 * @@ -12,6 +15,8 @@ public enum ProcessTaskEventEnum { PROCESS_TASK_ASSIGNED("process-task", "process-task-assigned", "流程任务已分配"), PROCESS_TASK_CREATED("process-task", "process-task-created", "流程任务已创建"), + PROCESS_TASK_TRANSFER("process-task", EVENTNAME_TRANSFER, "流程任务已转交"), + PROCESS_TASK_FALLBACK("process-task", EVENTNAME_FALLBACK, "流程任务已回退"), PROCESS_TASK_COMPLETED("process-task", "process-task-completed", "流程任务已结束"), PROCESS_TASK_DELETED("process-task", "process-task-deleted", "流程任务已删除"), ; @@ -45,4 +50,5 @@ public enum ProcessTaskEventEnum { public Event.EventCode getEventCode() { return eventCode; } + } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/SignApproverOrgLimitEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/SignApproverOrgLimitEnum.java new file mode 100644 index 000000000..922a108ea --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/SignApproverOrgLimitEnum.java @@ -0,0 +1,51 @@ +package cn.axzo.workflow.common.enums; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 签署确认节点的审批人层级范围限制 + * + * @author wangli + * @since 2025-03-26 14:30 + */ +public enum SignApproverOrgLimitEnum { + LV_0("LV_0", "当前组织", 0), + LV_1("LV_1", "上1级组织", 1), + LV_2("LV_2", "上2级组织", 2), + LV_3("LV_3", "上3级组织", 3), + LV_4("LV_4", "上4级组织", 4), + LV_5("LV_5", "上5级组织", 5), + ; + + private final String type; + + private final String desc; + + private final Integer code; + + SignApproverOrgLimitEnum(String type, String desc, Integer code) { + this.type = type; + this.desc = desc; + this.code = code; + } + + public String getType() { + return type; + } + + public String getDesc() { + return desc; + } + + public Integer getCode() { + return code; + } + + public static SignApproverOrgLimitEnum valueOfType(String type) { + return Arrays.stream(SignApproverOrgLimitEnum.values()) + .filter(i -> Objects.equals(i.getType(), type)) + .findAny() + .orElse(null); + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/SignApproverRoleLimitEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/SignApproverRoleLimitEnum.java new file mode 100644 index 000000000..2323fda54 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/SignApproverRoleLimitEnum.java @@ -0,0 +1,39 @@ +package cn.axzo.workflow.common.enums; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 签署确认节点的审批人层级范围限制 + * + * @author wangli + * @since 2025-03-26 14:30 + */ +public enum SignApproverRoleLimitEnum { + LEADER("LEADER", "负责人"), + ; + + private final String type; + + private final String desc; + + SignApproverRoleLimitEnum(String type, String desc) { + this.type = type; + this.desc = desc; + } + + public String getType() { + return type; + } + + public String getDesc() { + return desc; + } + + public static SignApproverRoleLimitEnum valueOfType(String type) { + return Arrays.stream(SignApproverRoleLimitEnum.values()) + .filter(i -> Objects.equals(i.getType(), type)) + .findAny() + .orElse(null); + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/VarTypeEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/VarTypeEnum.java new file mode 100644 index 000000000..bbfd8227f --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/VarTypeEnum.java @@ -0,0 +1,21 @@ +package cn.axzo.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * 模版上变量字段类型 + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +public enum VarTypeEnum { + + TEXT("text", "文本"), + + PICTURE("picture", "图片"); + + private String type; + private String desc; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/exception/WorkflowEngineException.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/exception/WorkflowEngineException.java index 57b915fd1..9a5e1f918 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/exception/WorkflowEngineException.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/exception/WorkflowEngineException.java @@ -19,6 +19,11 @@ public class WorkflowEngineException extends ServiceException { this.code = code.getRespCode(); } + public WorkflowEngineException(IRespCode code, Throwable cause, String... params) { + super(doFormat(code.getCode(), code.getMessage(), params), cause); + this.code = code.getRespCode(); + } + @Override public String getCode() { return this.code; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/AmountFieldDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/AmountFieldDTO.java new file mode 100644 index 000000000..d51003baf --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/AmountFieldDTO.java @@ -0,0 +1,33 @@ +package cn.axzo.workflow.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 金额字段值类型 + * + * @author wangli + * @since 2025-05-16 17:21 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class AmountFieldDTO implements Serializable { + private static final long serialVersionUID = -3519991080707599177L; + + /** + * 小写 + */ + private BigDecimal standardNumerals; + + /** + * 大写 + */ + private String uppercaseNumerals; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/BizDocDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/BizDocDTO.java new file mode 100644 index 000000000..1a110eaad --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/BizDocDTO.java @@ -0,0 +1,28 @@ +package cn.axzo.workflow.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 模型关联文档变更事件内部传输对象 + * + * @author wangli + * @since 2025-04-07 16:53 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BizDocDTO implements Serializable { + + private String archiveName; + + private String archiveCode; + + private Boolean enabled; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/ContactsPersonDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/ContactsPersonDTO.java new file mode 100644 index 000000000..2ff8b1e85 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/ContactsPersonDTO.java @@ -0,0 +1,69 @@ +package cn.axzo.workflow.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 通讯录组件返回的人员信息模型 + * + * @author wangli + * @since 2025-05-15 14:04 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ContactsPersonDTO { + /** + * xx:xx:xx + */ + private String nodeId; + + /** + * 身份 ID + */ + private Long identityId; + + /** + * 身份类型 + */ + private Integer identityType; + + /** + * 真实姓名 + */ + private String realName; + + /** + * 自然人 ID + */ + private Long personId; + + /** + * 人员所在的节点 ID + */ + private Long orgNodeId; + + /** + * 人员所在的单位 ID + */ + private Long ouId; + + /** + * 人员所在的单位名称 + */ + private String ouName; + + /** + * 人员所在的工作台 ID + */ + private Long workspaceId; + + /** + * 人员所在的工作台类型 + */ + private Integer workspaceType; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SignFileDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SignFileDTO.java new file mode 100644 index 000000000..852f91181 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SignFileDTO.java @@ -0,0 +1,60 @@ +package cn.axzo.workflow.common.model.dto; + +import cn.axzo.workflow.common.enums.FileTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 签署文件记录信息 + * + * @author wangli + * @since 2025-04-03 11:21 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SignFileDTO implements Serializable { + + private static final long serialVersionUID = -8709597975507074853L; + /** + * 文件模板主键 ID + */ + private Long id; + + /** + * 文件名称,可能会包含变量 + */ + private String fileName; + + /** + * 模板名称 + */ + private String templateName; + + /** + * 文件的标签 + */ + private String fileTag; + + /** + * 文件 code + */ + private String fileCode; + + /** + * 文件的类型 + */ + private FileTypeEnum fileType; + + /** + * 替换变量后的文件 fileKey + */ + private String fileKey; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SignatureDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SignatureDTO.java new file mode 100644 index 000000000..183722818 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SignatureDTO.java @@ -0,0 +1,45 @@ +package cn.axzo.workflow.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.List; + +/** + * 节点的电子签名数据 + * + * @author wangli + * @since 2025-01-15 20:11 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SignatureDTO implements Serializable { + private static final long serialVersionUID = 1L; + /** + * 节点 ID + */ + private String activityId; + /** + * 节点名称 + */ + private String activityName; + /** + * 签名图片数据有序集合 + */ + private List signatures; + + @Data + @Accessors(chain = true) + public static class SignDetail implements Serializable { + private static final long serialVersionUID = 1L; + private String signature; + private String advice; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SimpleDocDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SimpleDocDTO.java new file mode 100644 index 000000000..0210c2a47 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/SimpleDocDTO.java @@ -0,0 +1,37 @@ +package cn.axzo.workflow.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 审批人在不同任务下,关联文档的阅读状态 + * + * @author wangli + * @since 2025-04-08 13:59 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SimpleDocDTO implements Serializable { + /** + * 关联的文档 ID + */ + private Long id; + + /** + * 关联的文档业务标签 + */ + private String tag; + + /** + * 阅读状态: true 已读 + */ + private Boolean readStatus; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/TermNodeAddTimerJobDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/TermNodeAddTimerJobDTO.java new file mode 100644 index 000000000..d075b7194 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/TermNodeAddTimerJobDTO.java @@ -0,0 +1,26 @@ +package cn.axzo.workflow.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 业务节点添加定时任务的模型 + * + * @author wangli + * @since 2025-03-19 16:33 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class TermNodeAddTimerJobDTO implements Serializable { + private String processInstanceId; + private String activityId; + private String timeCycle; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/UploadFieldDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/UploadFieldDTO.java index 5093f46db..1eeede655 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/UploadFieldDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/UploadFieldDTO.java @@ -1,10 +1,12 @@ package cn.axzo.workflow.common.model.dto; -import com.alibaba.fastjson.JSON; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.io.Serializable; + /** * 上传附件字段类型 * @@ -14,9 +16,10 @@ import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor -@Deprecated // 这个类型不应该产生,建议后面迭代替换为 AttachmentDTO -public class UploadFieldDTO { +@Builder +public class UploadFieldDTO implements Serializable { + private static final long serialVersionUID = -1L; /** * 文件名称 */ @@ -25,24 +28,11 @@ public class UploadFieldDTO { /** * 文件类型 */ - private String fileType; + private String fileUrl; /** * 文件 oss 的 key */ private String fileKey; - public String toString() { - return JSON.toJSONString(this); - } - - public String toSpecString() { - return this.toString().replaceAll(",", "|"); - } - - public static UploadFieldDTO toObject(String str) { - str = str.trim().replaceAll("\\|",","); - return JSON.parseObject(str, UploadFieldDTO.class); - } - } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/VariableObjectDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/VariableObjectDTO.java new file mode 100644 index 000000000..4523937f7 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/VariableObjectDTO.java @@ -0,0 +1,44 @@ +package cn.axzo.workflow.common.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 从流程实例中获取变量,携带数据类型的对象 + * + * @author wangli + * @since 2025-04-09 11:46 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class VariableObjectDTO { + + public enum Type { + img, text, obj, signatureAndAdvice + } + + /** + * 变量 key + */ + private String key; + + /** + * 变量中文名 + */ + private String desc; + + /** + * 变量值 + */ + private Object value; + + /** + * 变量类型 + */ + @Builder.Default + private Type type = Type.text; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/es/HistoricProcessInstanceSearchForEsDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/es/HistoricProcessInstanceSearchForEsDTO.java index 9afc03396..d0e0cc31d 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/es/HistoricProcessInstanceSearchForEsDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/es/HistoricProcessInstanceSearchForEsDTO.java @@ -68,6 +68,7 @@ public class HistoricProcessInstanceSearchForEsDTO { *

* 默认是查询开始时间之后 */ + @Builder.Default private TimeQueryDirection startTimeDirection = TimeQueryDirection.AFTER; /** @@ -80,16 +81,19 @@ public class HistoricProcessInstanceSearchForEsDTO { *

* 默认是查询结束时间点之前 */ + @Builder.Default private TimeQueryDirection endTimeDirection = TimeQueryDirection.BEFORE; /** * 是否包含流程变量 */ + @Builder.Default private Boolean hasVariables = false; /** * 用于覆盖同步逻辑中的PageSize,一般不需要传 */ + @Builder.Default private Integer overPageSize = 50; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/print/FieldAttributeDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/print/FieldAttributeDTO.java new file mode 100644 index 000000000..0c1052145 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/print/FieldAttributeDTO.java @@ -0,0 +1,31 @@ +package cn.axzo.workflow.common.model.dto.print; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 字段属性 + * + * @author wangli + * @since 2025-01-16 17:31 + */ +@Data +@ApiModel("打印时的字段的扩展属性模型") +@NoArgsConstructor +@Accessors(chain = true) +public class FieldAttributeDTO { + + /** + * 属性 code + */ + private String code; + + /** + * 属性名称 + */ + private String name; + + private String fieldFormType; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/print/PrintFieldDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/print/PrintFieldDTO.java new file mode 100644 index 000000000..078b9c544 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/dto/print/PrintFieldDTO.java @@ -0,0 +1,46 @@ +package cn.axzo.workflow.common.model.dto.print; + +import cn.axzo.workflow.common.enums.PrintFieldCategoryEnum; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 打印时的字段模型 + * + * @author wangli + * @since 2025-01-16 17:09 + */ +@ApiModel("打印时的字段模型") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class PrintFieldDTO { + /** + * 字段中文名称 + */ + private String name; + /** + * 字段类型(表单字段、系统字段) + */ + private PrintFieldCategoryEnum fieldCategoryType; + + /** + * 用于替换是变量名 + */ + private String code; + + /** + * 字段的表单的类型 + */ + private String fieldFormType; + + /** + * 复杂组件的扩展字段,例如“自定义组件”,“审批日志”组件等 + */ + private List attributes; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmPageParam.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmPageParam.java index 91a566e5c..640809192 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmPageParam.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmPageParam.java @@ -10,6 +10,7 @@ import java.io.Serializable; @ApiModel("分页参数") public class BpmPageParam implements Serializable { + private static final long serialVersionUID = -7002013785995706695L; private static final Integer PAGE_NO = 1; private static final Integer PAGE_SIZE = 10; @ApiModelProperty(value = "页码,从 1 开始", required = true, example = "1") diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonMetaInfo.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonMetaInfo.java index 3bd00b61d..2b7ea5f75 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonMetaInfo.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonMetaInfo.java @@ -1,9 +1,12 @@ package cn.axzo.workflow.common.model.request.bpmn; +import cn.axzo.workflow.common.enums.BusinessTypeEnum; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; /** * 按钮元数据 @@ -15,6 +18,11 @@ import java.io.Serializable; @Accessors(chain = true) public class BpmnButtonMetaInfo implements Serializable { + public static final String BUTTON_TYPE_SYSTEM = "SYSTEM"; + public static final String BUTTON_TYPE_CUSTOM = "CUSTOM"; + + private static final long serialVersionUID = -5224316166904752829L; + /** * 按钮顺序 */ @@ -45,4 +53,9 @@ public class BpmnButtonMetaInfo implements Serializable { */ private String type; + /** + * 按钮支持的业务类型 + */ + private List supportBizType = new ArrayList<>(); + } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonModel.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonModel.java index 492adcf71..0c511be70 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonModel.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonModel.java @@ -27,6 +27,11 @@ public class BpmnJsonModel implements Serializable { @ApiModelProperty(value = "流程的 Json 结构") private BpmnJsonNode node; + /** + * 签署配置, 可为空,只有签署业务才有该配置 + */ + @ApiModelProperty(value = "签署配置") + private BpmnSignConf signConf; /** * 通知管理配置 */ diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java index 6b0681ba6..c9eeb68ef 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java @@ -45,13 +45,19 @@ public class BpmnJsonNodeProperty { //************* 审批人指定Start **************// /** * 审批人指定: position(指定岗位), role(指定角色), identity(指定身份), initiatorLeader(发起人主管), initiatorLeaderRecursion(发起人多级主管), - * fixedPerson(固定人员) + * fixedPerson(固定人员) initiatorSpecified(发起人自选) signerRelated(签署人相关组织) */ @ApiModelProperty(value = "任务节点: 审批人指定", notes = "position: 指定岗位, role: 指定角色, identity: 指定身份, initiatorLeader: " + - "发起人主管, initiatorLeaderRecursion: 发起人多级主管, fixedPerson: 固定人员") + "发起人主管, initiatorLeaderRecursion: 发起人多级主管, fixedPerson: 固定人员, initiatorSpecified: 发起人自选, signerRelated: 签署人相关组织") @NotBlank(message = "审批人指定不能为空") private ApproverSpecifyEnum approverSpecify; + /** + * 签署确认节点的审批人限定规则 + */ + @ApiModelProperty(value = "签署确认节点的审批人限定规则") + private BpmnSignApproverLimit signApproverLimit; + /** * 具体的配置值 *

@@ -94,6 +100,11 @@ public class BpmnJsonNodeProperty { private String emptyApproverSpecify; //************* 审批人为空时的策略End **************// + /** + * 电子签名开关 + */ + @ApiModelProperty(value = "电子签名") + private Boolean signature; /** * 按钮权限 diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignApproverLimit.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignApproverLimit.java new file mode 100644 index 000000000..d49918167 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignApproverLimit.java @@ -0,0 +1,44 @@ +package cn.axzo.workflow.common.model.request.bpmn; + +import cn.axzo.workflow.common.enums.SignApproverOrgLimitEnum; +import cn.axzo.workflow.common.enums.SignApproverRoleLimitEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * JSON 版本的 BPMN 协议模型中的确认人限定规则 + * + * @author wangli + * @since 2025-03-26 20:05 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的确认人限定规则") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnSignApproverLimit { + + /** + * 签署确认节点下人的组织限定规则 + *

+ * LV_0("LV_0", "当前组织"), + * LV_1("LV_1", "上1级组织"), + * LV_2("LV_2", "上2级组织"), + * LV_3("LV_3", "上3级组织"), + * LV_4("LV_4", "上4级组织"), + * LV_5("LV_5", "上5级组织"), + */ + @ApiModelProperty(value = "签署确认节点下人的组织限定规则") + private SignApproverOrgLimitEnum orgLimit; + + /** + * 签署确认节点下人的角色限定规则 + *

+ * INITIATOR_SPECIFIED("INITIATOR_SPECIFIED", "发起人自选"), + * LEADER("LEADER", "负责人"), + */ + @ApiModelProperty(value = "签署确认节点下人的角色限定规则") + private SignApproverRoleLimitEnum roleLimit; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignConf.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignConf.java new file mode 100644 index 000000000..f88d150ba --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignConf.java @@ -0,0 +1,40 @@ +package cn.axzo.workflow.common.model.request.bpmn; + +import cn.axzo.workflow.common.enums.BpmnSignType; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 流程定义中的签署配置 + * + * @author wangli + * @since 2025-03-25 16:56 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的签署管理") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnSignConf implements Serializable { + + private static final long serialVersionUID = -6015492451455020117L; + + /** + * 签署方式 + *

+ * SINGLE("SINGLE", "指定人群,所有人共同签署一份文件"), + * MULTI("MULTI", "指定人群,每人签署一份文件"), + */ + @ApiModelProperty(value = "签署方式") + private BpmnSignType signType; + + /** + * 签署业务待办模板 + */ + @ApiModelProperty(value = "签署业务待办模板") + private BpmnSignPendingProperty signPendingProperty; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignPendingProperty.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignPendingProperty.java new file mode 100644 index 000000000..89da2289b --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnSignPendingProperty.java @@ -0,0 +1,37 @@ +package cn.axzo.workflow.common.model.request.bpmn; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * JSON 版本的 BPMN 协议模型中的签署的待办 + * + * @author wangli + * @since 2025-03-25 17:09 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的签署的待办") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnSignPendingProperty implements Serializable { + private static final long serialVersionUID = -6015492451455020117L; + + /** + * 待办消息模板 ID + */ + @ApiModelProperty(value = "待办消息模板 ID") + @NotBlank(message = "待办消息模板 ID 不能为空") + private String pendingMessageId; + + /** + * 用于前端回显数据, 服务端不解析 + */ + @ApiModelProperty(value = "用于前端回显数据, 服务端不解析") + private String viewJson; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelCreateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelCreateDTO.java index 91c8e401f..e2833ef2f 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelCreateDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelCreateDTO.java @@ -20,6 +20,8 @@ import java.io.Serializable; @Accessors(chain = true) public class BpmnModelCreateDTO implements Serializable { + private static final long serialVersionUID = 1383240088010310865L; + @ApiModelProperty(value = "流程模型标识", example = "process_key", hidden = true) @Length(max = 255, message = "流程标识最长只支持255个字符") private String key; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelUpdateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelUpdateDTO.java index f7826dd46..7e5e7d735 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelUpdateDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/BpmnModelUpdateDTO.java @@ -16,6 +16,8 @@ import lombok.experimental.Accessors; @Accessors(chain = true) public class BpmnModelUpdateDTO extends BpmnModelCreateDTO { + private static final long serialVersionUID = 3661214238064751897L; + @ApiModelProperty(value = "流程模型 ID", required = true) private String processModelId; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocByIdDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocByIdDTO.java new file mode 100644 index 000000000..c1603a14c --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocByIdDTO.java @@ -0,0 +1,27 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 获取文档 + * + * @author wangli + * @since 2025-04-16 20:18 + */ +@ApiModel("获取文档") +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DocByIdDTO { + + private List ids; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocCloneDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocCloneDTO.java new file mode 100644 index 000000000..0ba48428d --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocCloneDTO.java @@ -0,0 +1,40 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 克隆文档 + * + * @author wangli + * @since 2025-04-18 11:30 + */ +@ApiModel("克隆文档") +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DocCloneDTO { + /** + * 文档 ID + */ + private Long docId; + /** + * 克隆好的文档归属模型 ID + */ + private String targetModelId; + /** + * 克隆好的文档归属租户 + */ + private String targetTenantId; + + /** + * 克隆好的文档的 tag + */ + private String targetFileTag; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocCreateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocCreateDTO.java new file mode 100644 index 000000000..ad264818b --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocCreateDTO.java @@ -0,0 +1,121 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import cn.axzo.workflow.common.enums.FileTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; + +/** + * 创建模型关联的文档 + * + * @author wangli + * @since 2025-03-27 14:50 + */ +@ApiModel("创建模型关联的文档") +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class DocCreateDTO implements Serializable { + + /** + * 关联到的模型 ID + */ + @ApiModelProperty(value = "文档被关联到的模型 ID") + @NotBlank(message = "模型 ID 不能为空") + private String modelId; + + /** + * 模型对应业务 KEY + */ + @ApiModelProperty(value = "模型对应的业务 ID") + @NotBlank(message = "模型 KEY 不能为空") + private String modelKey; + /** + * 文档关联ID + *

+ * word/excel 对应 wps 的 Id, + * hp 对应是content 表的主键(前端新建时不回传) + * pdf 对应 oss 中的 fileKey + */ + @ApiModelProperty(value = "底层文件关联标识") + private String fileRelationId; + + /** + * 文件名称 + */ + @ApiModelProperty(value = "文件名称") + private String fileName; + + /** + * 模板名称 + */ + @ApiModelProperty(value = "模板名称") + private String templateName; + + /** + * 自动归档位置(本期不实现) + */ + private String location; + + /** + * 文档类型 + *

+ * WORD("word", "文本"), + * EXCEL("excel", "表格"), + * HIPRINT("hiprint", "智能文档"), + * PDF("pdf", "PDF"), + */ + @ApiModelProperty(value = "文件类型") + private FileTypeEnum fileType; + + /** + * 业务标签 + */ + @ApiModelProperty(value = "业务标签") + private String tag; + + /** + * 启用状态 + */ + @Builder.Default + @ApiModelProperty(value = "启用状态") + private Boolean status = true; + + /** + * 是否是临时文件 + */ + @Builder.Default + @ApiModelProperty(value = "是否是临时文件,默认 false") + private Boolean tempFile = false; + /** + * 是否必选 + */ + @Builder.Default + @ApiModelProperty(value = "是否必选") + private Boolean require = false; + + /** + * HiPrint文件类型,需要回传 + */ + @ApiModelProperty(value = "HiPrint文件类型,需要回传,其他类型无视该字段") + private String content; + + /** + * 租户 ID + */ + @ApiModelProperty(value = "租户 ID") + @Builder.Default + private String tenantId = NO_TENANT_ID; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocOrderDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocOrderDTO.java new file mode 100644 index 000000000..ae24e1c55 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocOrderDTO.java @@ -0,0 +1,36 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import cn.axzo.workflow.common.enums.OrderEnum; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +/** + * 模型关联的文档排序 + * + * @author wangli + * @since 2025-03-31 16:32 + */ +@ApiModel("模型关联的文档排序") +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class DocOrderDTO implements Serializable { + private static final long serialVersionUID = -4783444148609103421L; + /** + * 文档主键 ID + */ + private Long id; + + /** + * 操作顺序 + */ + private OrderEnum order; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocQueryDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocQueryDTO.java new file mode 100644 index 000000000..a709c868a --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocQueryDTO.java @@ -0,0 +1,44 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 流程关联文档搜索入参模型 + * + * @author wangli + * @since 2025-03-31 09:46 + */ +@ApiModel("流程关联文档搜索入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DocQueryDTO { + + /** + * 流程实例 ID + */ + @ApiModelProperty(value = "流程实例 ID") + private String processInstanceId; + + /** + * 流程定义 KEY(业务 ID) + */ + @ApiModelProperty(value = "流程定义 KEY(业务 ID)") + private String processDefinitionKey; + + /** + * 租户 ID,对应工作台 ID + */ + @ApiModelProperty(value = "租户 ID,对应工作台 ID") + private String tenantId; + + @ApiModelProperty(value = "是否包含临时文档") + @Builder.Default + private Boolean tempFile = false; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocResetDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocResetDTO.java new file mode 100644 index 000000000..08b571111 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocResetDTO.java @@ -0,0 +1,29 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@ApiModel("流程关联文档重置入参模型") +@Data +public class DocResetDTO { + + /** + * 业务 ID + */ + @NotBlank(message = "业务 ID 不能为空") + private String category; + + /** + * 代运营模型 ID + */ + @NotBlank(message = "模型 ID 不能为空") + private String modelId; + + /** + * 代运营的单位或租户ID + */ + @NotBlank(message = "工作台 ID 不能为空") + private String workspaceId; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocSearchDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocSearchDTO.java new file mode 100644 index 000000000..8eece5314 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocSearchDTO.java @@ -0,0 +1,34 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import cn.axzo.workflow.common.model.request.BpmPageParam; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; + +/** + * 流程关联文档搜索入参模型 + * + * @author wangli + * @since 2025-03-31 09:46 + */ +@EqualsAndHashCode(callSuper = true) +@ApiModel("流程关联文档搜索入参模型") +@Data +public class DocSearchDTO extends BpmPageParam { + + private static final long serialVersionUID = -308388315001754954L; + /** + * 流程模型 ID + */ + @ApiModelProperty(value = "模型 ID") + private String modelId; + + @ApiModelProperty(value = "租户 ID") + private String tenantId = NO_TENANT_ID; + + @ApiModelProperty(value = "是否包含临时文档") + private Boolean tempFile = false; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocStatusDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocStatusDTO.java new file mode 100644 index 000000000..ba091cf13 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocStatusDTO.java @@ -0,0 +1,32 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 流程关联文档状态变更入参模型 + * + * @author wangli + * @since 2025-04-07 11:44 + */ +@ApiModel("流程关联文档状态变更入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DocStatusDTO { + /** + * 文档 ID + */ + @ApiModelProperty(value = "文档 ID") + private Long id; + + /** + * 状态 true 启用, false 停用 + */ + private Boolean status; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocTenantQueryDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocTenantQueryDTO.java new file mode 100644 index 000000000..3a3cb16b7 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocTenantQueryDTO.java @@ -0,0 +1,30 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * 查询指定业务下设置过关联文档的代运营项目集合入参 + * + * @author wangli + * @since 2025-04-10 17:57 + */ +@ApiModel("查询指定业务下设置过关联文档的代运营项目集合入参") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class DocTenantQueryDTO { + + /** + * 业务ID + */ + @NotBlank(message = "业务 ID 不能为空") + private String processDefinitionKey; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocUpdateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocUpdateDTO.java new file mode 100644 index 000000000..ef270fa99 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/model/doc/DocUpdateDTO.java @@ -0,0 +1,30 @@ +package cn.axzo.workflow.common.model.request.bpmn.model.doc; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; + +/** + * 创建模型关联的文档 + * + * @author wangli + * @since 2025-03-27 14:50 + */ +@ApiModel("创建模型关联的文档") +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class DocUpdateDTO extends DocCreateDTO implements Serializable { + + @ApiModelProperty(value = "主键ID") + private Long id; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintFieldQueryDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintFieldQueryDTO.java new file mode 100644 index 000000000..3775f63e5 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintFieldQueryDTO.java @@ -0,0 +1,44 @@ +package cn.axzo.workflow.common.model.request.bpmn.print; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * 获取指定打印模板可打印的字段集合的查询入参模型 + * + * @author wangli + * @since 2025-01-16 17:42 + */ +@ApiModel("获取指定打印模板可打印的字段集合的查询入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PrintFieldQueryDTO { + + /** + * 模板定义 KEY + */ + @ApiModelProperty(value = "流程模板 KEY") + @NotBlank(message = "流程模板 KEY 不能为空") + private String processDefinitionKey; + + /** + * 租户 ID + */ + @ApiModelProperty(value = "租户 ID") + private String tenantId; + + /** + * 是否抛出内部异常 + */ + @ApiModelProperty(value = "是否报错内部异常") + @Builder.Default + private Boolean throwException = true; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintTemplateConfigQueryDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintTemplateConfigQueryDTO.java new file mode 100644 index 000000000..c5e4503b6 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintTemplateConfigQueryDTO.java @@ -0,0 +1,48 @@ +package cn.axzo.workflow.common.model.request.bpmn.print; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; + +/** + * 获取打印模板配置内容入参模型 + * + * @author wangli + * @since 2025-01-20 16:53 + */ +@ApiModel("获取打印模板配置内容入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PrintTemplateConfigQueryDTO { + + /** + * 审批模型 ID + */ + @ApiModelProperty(value = "审批模型 ID") + private String modelId; + + /** + * 流程实例 ID + */ + @ApiModelProperty(value = "流程实例 ID") + private String processInstanceId; + + /** + * 业务 ID + */ + @ApiModelProperty(value = "业务 ID") + private String processDefinitionKey; + + /** + * 租户 ID,配置 processDefinitionKey 使用 + */ + @ApiModelProperty(value = "租户 ID") + private String tenantId = NO_TENANT_ID; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintTemplateConfigUpsertDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintTemplateConfigUpsertDTO.java new file mode 100644 index 000000000..432d33d31 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/PrintTemplateConfigUpsertDTO.java @@ -0,0 +1,46 @@ +package cn.axzo.workflow.common.model.request.bpmn.print; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 打印模板配置内容入参模型 + * + * @author wangli + * @since 2025-01-20 16:44 + */ +@ApiModel("打印模板配置内容入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class PrintTemplateConfigUpsertDTO { + + /** + * 审批模型 ID + */ + @ApiModelProperty("审批模型 ID") + private String modelId; + + /** + * 打印文件名称 + */ + @ApiModelProperty(value = "打印文件名称") + private String printFileName; + + /** + * 打印模板名称 + */ + @ApiModelProperty(value = "打印模板名称") + private String printTemplateName; + + /** + * 打印模板配置内容 + */ + @ApiModelProperty(value = "打印模板配置内容") + private String printTemplateConfig; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/RestPrintTemplateConfigDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/RestPrintTemplateConfigDTO.java new file mode 100644 index 000000000..d05f50c1e --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/print/RestPrintTemplateConfigDTO.java @@ -0,0 +1,39 @@ +package cn.axzo.workflow.common.model.request.bpmn.print; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * 重置打印模型(代运营专用) + * + * @author wangli + * @since 2025-01-20 19:55 + */ +@ApiModel("打印模板配置内容入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RestPrintTemplateConfigDTO { + + /** + * 模板的定义 ID + */ + @ApiModelProperty(value = "模板的定义 ID") + @NotBlank(message = "模型定义 ID 不能为空") + private String processDefinitionKey; + + /** + * 代运营模型 ID + */ + @ApiModelProperty(value = "代运营模型 ID") + @NotBlank(message = "代运营模板 ID不能为空") + private String modelId; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BeforeProcessInstanceCreateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BeforeProcessInstanceCreateDTO.java new file mode 100644 index 000000000..f5e5a90f8 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BeforeProcessInstanceCreateDTO.java @@ -0,0 +1,68 @@ +package cn.axzo.workflow.common.model.request.bpmn.process; + +import cn.axzo.workflow.common.enums.BpmnFlowNodeType; +import cn.axzo.workflow.common.model.dto.CooperationOrgDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * 创建审批流程前的入参模型 + * + * @author wangli + * @since 2025-01-15 10:21 + */ +@ApiModel("创建流程实例前的入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BeforeProcessInstanceCreateDTO { + /** + * 流程定义的标识 + *

+ * [对应 OMS 系统中审批业务的业务 ID] + */ + @ApiModelProperty(value = "业务 ID") + @NotEmpty(message = "流程定义的标识不能为空") + private String processDefinitionKey; + + /** + * 发起流程实例归属租户 ID + *

+ * 为空时,默认是编辑公共流程模型, 如果是代运营创建,则必填 + *

建议都传值,在安心筑中对应工作台 ID

+ */ + @ApiModelProperty(value = "发起的审批是属于哪个租户") + @NotBlank(message = "工作台 ID 不能为空") + private String tenantId; + + /** + * 组织关系 + *

+ * 用于流程引擎计算对应节点的审批人, 例如第一个审批节点配置的是劳务分包的岗位,第二个审批节点配置的专业分包的角色, + * 那么, 组织关系就需要传入劳务分包的信息以及专业分包的信息,如果还有更多的审批节点配置,以此类推. + * 同时,该属性还会用于计算抄送人,以及消息通知的目标接收人 + */ + @ApiModelProperty(value = "组织关系") + private CooperationOrgDTO cooperationOrg; + + @ApiModelProperty(value = "发起人信息") + @Valid + private BpmnTaskDelegateAssigner initiator; + + /** + * 最后响应的结果中,过滤出需要的类型 + */ + @ApiModelProperty(value = "流程节点过滤类型") + private List filter; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAbortDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAbortDTO.java index a98fcdebf..474fdf003 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAbortDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAbortDTO.java @@ -98,5 +98,6 @@ public class BpmnProcessInstanceAbortDTO { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = true; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAdminPageReqVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAdminPageReqVO.java index bfc30538a..09f65b23c 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAdminPageReqVO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceAdminPageReqVO.java @@ -4,6 +4,7 @@ import cn.axzo.workflow.common.model.request.BpmPageParam; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.ToString; import java.util.Date; @@ -15,11 +16,13 @@ import java.util.List; * @author wangli * @since 06/03/2024 2:35 pm */ +@EqualsAndHashCode(callSuper = true) @ApiModel("用于超管查询所有的流程实例入参模型") @Data @ToString(callSuper = true) public class BpmnProcessInstanceAdminPageReqVO extends BpmPageParam { + private static final long serialVersionUID = 5051264217867881829L; /** * 流程实例 ID */ diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java index e8234cf80..0296fc6b8 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCancelDTO.java @@ -5,12 +5,13 @@ import cn.axzo.workflow.common.constraint.AttachmentValidator; import cn.axzo.workflow.common.enums.AttachmentTypeEnum; import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.common.valid.group.ValidGroup; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import javax.validation.Valid; import javax.validation.constraints.NotBlank; @@ -28,21 +29,21 @@ import java.util.List; @Data @AllArgsConstructor @NoArgsConstructor -@Builder +@SuperBuilder public class BpmnProcessInstanceCancelDTO { /** * 流程实例的编号 */ @ApiModelProperty(value = "流程实例编号", example = "11") - @NotBlank(message = "流程实例编号不能为空") + @NotBlank(message = "流程实例编号不能为空", groups = {ValidGroup.Insert.class, ValidGroup.Update.class}) private String processInstanceId; /** * 工作台 ID */ @ApiModelProperty(value = "工作台 ID") - @NotBlank(message = "工作台不能为空") + @NotBlank(message = "工作台不能为空", groups = ValidGroup.Insert.class) private String tenantId; /** @@ -55,12 +56,12 @@ public class BpmnProcessInstanceCancelDTO { * 附件列表 */ @ApiModelProperty(value = "附件列表") - @Size(max = 11, message = "附件数量超过限制") + @Size(max = 11, message = "附件数量超过限制", groups = {ValidGroup.Insert.class, ValidGroup.Update.class}) @AttachmentValidator(types = { @AttachmentTypeValidator(type = AttachmentTypeEnum.image, max = 5), @AttachmentTypeValidator(type = AttachmentTypeEnum.file, max = 5), @AttachmentTypeValidator(type = AttachmentTypeEnum.signature, max = 1)} - ) + , groups = {ValidGroup.Insert.class, ValidGroup.Update.class}) private List attachmentList; /** @@ -75,7 +76,7 @@ public class BpmnProcessInstanceCancelDTO { */ @ApiModelProperty(value = "审批流程发起人信息") @Valid - @NotNull(message = "审批流程的发起人不能为空") + @NotNull(message = "审批流程的发起人不能为空", groups = {ValidGroup.Insert.class, ValidGroup.Update.class}) private BpmnTaskDelegateAssigner initiator; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java index b92ae5fe7..2c1a7e542 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateDTO.java @@ -1,5 +1,6 @@ package cn.axzo.workflow.common.model.request.bpmn.process; +import cn.axzo.workflow.common.constant.BpmnConstants; import cn.axzo.workflow.common.model.dto.CooperationOrgDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import io.swagger.annotations.ApiModel; @@ -14,6 +15,7 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -50,14 +52,28 @@ public class BpmnProcessInstanceCreateDTO extends BpmnProcessInstanceCreateWithF @NotBlank(message = "工作台 ID 不能为空") private String tenantId; + /** + * 流程在发起时指定审批人 + */ + @ApiModelProperty(value = "发起时指定节点的审批人") + private Map> specifyAssignerMap; + /** * 流程实例关联的变量 */ + @Builder.Default private Map variables = new HashMap<>(); + /** + * 业务管理中定义变量的入参 + */ + @Builder.Default + private Map bizCustomVariables = new HashMap<>(); + /** * 待办相关的变量,如路由参数, 模板参数等等 */ + @Builder.Default private Map pendingVariables = new HashMap<>(); /** @@ -94,6 +110,19 @@ public class BpmnProcessInstanceCreateDTO extends BpmnProcessInstanceCreateWithF * 是否异步执行,该异步是引擎的一种运行模式 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = true; + /** + * 签署业务,发起时,可基于模型启用的模板,再勾选使用本次发起时选择的文档标签 + */ + @ApiModelProperty(value = "签署业务发起时,选择的文档") + private List docIds; + + /** + * 仅针对签署业务,设置审批完成后的最终签署人列表,该属性仅为透传,业务消费时,请从 MQ 广播事件中的 variables 中通过 key= {@link BpmnConstants#SIGNATORIES } 获取 + */ + @ApiModelProperty(value = "签署业务,最终签署人") + private List signatories; + } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateWithFormDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateWithFormDTO.java index c3bd147bb..c983e8d45 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateWithFormDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceCreateWithFormDTO.java @@ -1,12 +1,10 @@ package cn.axzo.workflow.common.model.request.bpmn.process; +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import lombok.EqualsAndHashCode; -import javax.validation.constraints.NotBlank; -import java.util.HashMap; import java.util.Map; /** @@ -17,10 +15,23 @@ import java.util.Map; */ @ApiModel("创建工作流实例同时携带表单的入参模型") @Data -public class BpmnProcessInstanceCreateWithFormDTO { +public class BpmnProcessInstanceCreateWithFormDTO { + /** + * 审批使用了表单,请一定注意传参 + *

+ * 图片类型和附件类型组件:请用 @see {@link UploadFieldDTO} 对象集合传入 + *

+     *     // form_image 为表单项的 key, value 为 UploadFileDTO 对象集合,如果前端使用了组件,一般建议回传所有属性;特殊情况下可以只传 fileUrl
+     *     "form_image": [{
+     *          "fileName": "",
+     *          "fileUrl": "http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960",
+     *          "fileKey": 123
+     *      }]
+     * 
+ */ @ApiModelProperty(value = "通过表单创建流程时传入的初始表单数据") - private Map startFormVariables = new HashMap<>(); + private Map startFormVariables; /** * 工作流实例集成表单后,可以通过表单 key 组装成的变量存入该变量的值,可用于后续流程的流转 *

diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceLogQueryDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceLogQueryDTO.java index 17e7ad583..e0a4841d7 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceLogQueryDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceLogQueryDTO.java @@ -44,6 +44,7 @@ public class BpmnProcessInstanceLogQueryDTO { * 返回结果中是否包含按钮 */ @ApiModelProperty(value = "返回结果中是否包含按钮", notes = "如果访问者为空,该属性为 true 时,同样也不会返回按钮") + @Builder.Default private Boolean hasButton = false; /** diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceVariablesDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceVariablesDTO.java new file mode 100644 index 000000000..80ba346d2 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/BpmnProcessInstanceVariablesDTO.java @@ -0,0 +1,24 @@ +package cn.axzo.workflow.common.model.request.bpmn.process; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 获取流程实例中变量集合的入参模型 + * + * @author wangli + * @since 2025-01-16 17:05 + */ +@ApiModel("获取流程实例中变量集合的入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BpmnProcessInstanceVariablesDTO { + + private String processInstanceId; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/SuperBpmnProcessInstanceCancelDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/SuperBpmnProcessInstanceCancelDTO.java new file mode 100644 index 000000000..7773be65e --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/SuperBpmnProcessInstanceCancelDTO.java @@ -0,0 +1,26 @@ +package cn.axzo.workflow.common.model.request.bpmn.process; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * 取消流程实例的入参模型 + * + * @author wangli + * @since 2023/7/17 09:34 + */ +@EqualsAndHashCode(callSuper = true) +@ApiModel("取消流程实例的入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class SuperBpmnProcessInstanceCancelDTO extends BpmnProcessInstanceCancelDTO { + + private Boolean superAdmin; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ApproverReadStatusDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ApproverReadStatusDTO.java new file mode 100644 index 000000000..3c7d1b9f5 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ApproverReadStatusDTO.java @@ -0,0 +1,40 @@ +package cn.axzo.workflow.common.model.request.bpmn.process.doc; + +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 签署业务流程实例,查询审批的关联文档阅读状态入参 + * + * @author wangli + * @since 2025-04-08 11:27 + */ +@ApiModel("签署业务流程实例,查询审批的关联文档阅读状态入参") +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class ApproverReadStatusDTO { + + /** + * 流程实例 ID + */ + @ApiModelProperty(value = "流程实例 ID") + @NotBlank(message = "流程实例 ID 不能为空") + private String processInstanceId; + + /** + * 访问人 + */ + @ApiModelProperty(value = "访问人") + @NotNull(message = "访问人信息不能为空") + private BpmnTaskDelegateAssigner assigner; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ChangeApproverReadStatusDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ChangeApproverReadStatusDTO.java new file mode 100644 index 000000000..3d04bbbde --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ChangeApproverReadStatusDTO.java @@ -0,0 +1,36 @@ +package cn.axzo.workflow.common.model.request.bpmn.process.doc; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import javax.validation.constraints.NotNull; + +/** + * 签署业务流程实例,更新审批的关联文档阅读状态入参 + * + * @author wangli + * @since 2025-04-08 14:50 + */ +@EqualsAndHashCode(callSuper = true) +@ApiModel("签署业务流程实例,更新审批的关联文档阅读状态入参") +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class ChangeApproverReadStatusDTO extends ApproverReadStatusDTO { + /** + * 文档ID + */ + @ApiModelProperty(value = "文档 ID") + @NotNull(message = "文档 ID 不能为空") + private Long docId; + + @ApiModelProperty(value = "阅读状态") + @NotNull(message = "阅读状态不能为空") + private Boolean readStatus; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ProcessDocQueryDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ProcessDocQueryDTO.java new file mode 100644 index 000000000..6d6a21d06 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/process/doc/ProcessDocQueryDTO.java @@ -0,0 +1,39 @@ +package cn.axzo.workflow.common.model.request.bpmn.process.doc; + +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * 签署业务流程实例,在审批待办中查询文档列表的入参 + * + * @author wangli + * @since 2025-04-03 16:30 + */ +@ApiModel("签署业务流程实例,在审批待办中查询文档列表的入参") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ProcessDocQueryDTO { + + /** + * 流程实例 ID + */ + @ApiModelProperty(value = "流程实例 ID") + @NotBlank(message = "流程实例 ID 不能为空") + private String processInstanceId; + + /** + * 只有当该属性传值后,响应模型中才会返回阅读状态信息 + */ + @ApiModelProperty(value = "访问者") + private BpmnTaskDelegateAssigner assigner; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java index 0aa49fac4..e0995aed8 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/AttachmentDTO.java @@ -65,4 +65,10 @@ public class AttachmentDTO implements Serializable { @IndexField(fieldType = FieldType.KEYWORD) private String url; + /** + * 文件 OSS 的 fileKey + */ + @ApiModelProperty(value = "附件 oss 的 fileKey") + private String fileKey; + } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java index 0fbf1924c..baa69fe44 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java @@ -7,7 +7,6 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import java.util.List; @@ -62,12 +61,14 @@ public class BpmnActivitySetAssigneeDTO { *

* 如果业务不能接受,请自己主动去重后提交. */ + @Builder.Default private Boolean serverSideDeduplication = false; /** * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = false; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivityTriggerDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivityTriggerDTO.java index bc7356233..be10451a9 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivityTriggerDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivityTriggerDTO.java @@ -36,6 +36,7 @@ public class BpmnActivityTriggerDTO implements Serializable { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = true; /** diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnNodeBackSystemOperateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnNodeBackSystemOperateDTO.java new file mode 100644 index 000000000..c01572e98 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnNodeBackSystemOperateDTO.java @@ -0,0 +1,67 @@ +package cn.axzo.workflow.common.model.request.bpmn.task; + +import cn.axzo.workflow.common.constraint.AttachmentTypeValidator; +import cn.axzo.workflow.common.constraint.AttachmentValidator; +import cn.axzo.workflow.common.enums.AttachmentTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.io.Serializable; +import java.util.List; + +/** + * 审批节点退回模型 + * + */ +@ApiModel("审批节点退回模型") +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +public class BpmnNodeBackSystemOperateDTO implements Serializable { + + private static final long serialVersionUID = -4160538355403179298L; + + @ApiModelProperty(value = "流程实例id", required = true) + @NotBlank(message = "流程实例id不能为空") + private String processInstanceId; + + @ApiModelProperty(value = "当前流程节点id", required = true) + @NotBlank(message = "当前节点id不能为空") + private String currentActivityId; + + @ApiModelProperty(value = "目标流程节点id", required = true) + @NotBlank(message = "目标流程节点id不能为空") + private String toActivityId; + + @ApiModelProperty(value = "可以指定任务处理", required = true) + private List targetTaskIds; + + @ApiModelProperty(value = "意见") + private String advice; + + @ApiModelProperty(value = "操作描述") + private String operationDesc; + + @ApiModelProperty(value = "操作人") + private BpmnTaskDelegateAssigner operator; + + /** + * 附件列表 + */ + @ApiModelProperty(value = "附件列表") + @Size(max = 11, message = "附件数量超过限制") + @AttachmentValidator(types = { + @AttachmentTypeValidator(type = AttachmentTypeEnum.image, max = 5), + @AttachmentTypeValidator(type = AttachmentTypeEnum.file, max = 5), + @AttachmentTypeValidator(type = AttachmentTypeEnum.signature, max = 1)} + ) + private List attachmentList; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCompleteDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCompleteDTO.java index cc1b5c404..905c68fe4 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCompleteDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCompleteDTO.java @@ -71,5 +71,6 @@ public class BpmnRobotTaskCompleteDTO { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = false; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCreateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCreateDTO.java index 67a3e5068..f84e15c7c 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCreateDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnRobotTaskCreateDTO.java @@ -51,5 +51,6 @@ public class BpmnRobotTaskCreateDTO { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = false; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditDTO.java index 91fedef41..64f01c435 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditDTO.java @@ -78,6 +78,7 @@ public class BpmnTaskAuditDTO { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = true; /** diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditWithFormDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditWithFormDTO.java index 0f324007c..89c35023c 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditWithFormDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskAuditWithFormDTO.java @@ -20,12 +20,14 @@ public class BpmnTaskAuditWithFormDTO extends BpmnTaskAuditDTO { /** * 表单数据 + *

+ * 必须为全量表单数据,不能只提交部分表单数据 */ @ApiModelProperty(value = "表单数据") private Map formVariables; /** - * 暂对不对 + * 暂对不用 */ private String outcome; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskButtonSearchDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskButtonSearchDTO.java new file mode 100644 index 000000000..dc32d16c7 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskButtonSearchDTO.java @@ -0,0 +1,27 @@ +package cn.axzo.workflow.common.model.request.bpmn.task; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +@Data +@ApiModel("获取审批任务按钮,状态入参模型") +public class BpmnTaskButtonSearchDTO implements Serializable { + + private static final long serialVersionUID = -3220083731019329293L; + + @ApiModelProperty(value = "流程实例id", required = true) + @NotBlank(message = "流程实例id不能为空") + private String processInstanceId; + + @ApiModelProperty(value = "审批任务id", required = true) + @NotBlank(message = "审批任务id不能为空") + private String taskId; + + private Long initiatorPersonId; + + private Long executorPersonId; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCommentDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCommentDTO.java index 665a5af86..4d5be875d 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCommentDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCommentDTO.java @@ -77,6 +77,7 @@ public class BpmnTaskCommentDTO implements Serializable { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = false; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCountersignDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCountersignDTO.java index 3b67b9860..9bdc7c69b 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCountersignDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskCountersignDTO.java @@ -89,6 +89,7 @@ public class BpmnTaskCountersignDTO implements Serializable { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = true; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskDelegateAssigner.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskDelegateAssigner.java index 4316baeeb..286bf2b1f 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskDelegateAssigner.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskDelegateAssigner.java @@ -1,6 +1,7 @@ package cn.axzo.workflow.common.model.request.bpmn.task; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import lombok.AllArgsConstructor; import lombok.Builder; @@ -108,6 +109,18 @@ public class BpmnTaskDelegateAssigner implements Serializable { @IndexField(exist = false) private String avatar; + /** + * 人员对应的 nodeId,如果在审批模板中对应节点未使用流程自主查询审批人的功能(如指定岗位/身份/角色之类),则需要业务方主动传入 + */ + private String nodeId; + + /** + * 扩展字段, 业务无需关系 + *

+ * 仅用于日志展示人的一些状态信息 + */ + private JSONObject ext; + public final String buildAssigneeId_1_2_1() { if (StringUtils.hasLength(assigneeType)) { return tenantId + "|" + assignee + "|" + assigneeType; @@ -136,6 +149,12 @@ public class BpmnTaskDelegateAssigner implements Serializable { this.tenantId = tenantId; } + public BpmnTaskDelegateAssigner(String assignee, String assigneeType, String assignerName, String personId, String tenantId, String ouId, String avatar, String nodeId) { + this.assignerName = assignerName; + this.personId = personId; + this.tenantId = tenantId; + } + public final boolean comparePersonIdToOther(BpmnTaskDelegateAssigner other) { return Objects.equals(personId, other.getPersonId()); } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskRemindDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskRemindDTO.java index 7f51ba85b..806bd51b1 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskRemindDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskRemindDTO.java @@ -2,10 +2,13 @@ package cn.axzo.workflow.common.model.request.bpmn.task; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; import java.util.List; /** @@ -16,8 +19,16 @@ import java.util.List; */ @ApiModel("催办功能入参模型") @Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@Builder public class BpmnTaskRemindDTO { - + /** + * 操作催办时的终端类型,管理端cmp、工人端cm + */ + @NotBlank(message = "终端类型不能为空") + private String terminalType; /** * 审批节点唯一标识 */ @@ -27,9 +38,9 @@ public class BpmnTaskRemindDTO { /** * 催办方式 *

- * 站内信:notice 短信:sms + * IM, 如果为空,默认是 IM */ - @NotEmpty(message = "催办方式不能为空") + @ApiModelProperty(value = "催办方式", example = "im") private List remindTypes; /** @@ -40,8 +51,9 @@ public class BpmnTaskRemindDTO { private String processInstanceId; /** - * 租户 ID + * 是否异步执行 */ - @ApiModelProperty("租户 ID") - private String tenantId; + @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default + private Boolean async = true; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskTransferDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskTransferDTO.java index 9a4a64877..dc4c0f7c8 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskTransferDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnTaskTransferDTO.java @@ -79,6 +79,7 @@ public class BpmnTaskTransferDTO implements Serializable { * 是否异步执行 */ @ApiModelProperty(value = "是否异步", notes = "异步时,只接收请求便返回数据") + @Builder.Default private Boolean async = true; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/ExtHiTaskSearchDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/ExtHiTaskSearchDTO.java index e88d62a65..a16d170dc 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/ExtHiTaskSearchDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/ExtHiTaskSearchDTO.java @@ -3,7 +3,12 @@ package cn.axzo.workflow.common.model.request.bpmn.task; import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; /** * 扩展的历史任务表搜索入参模型 @@ -13,6 +18,9 @@ import lombok.Data; */ @ApiModel("扩展的历史任务表搜索入参模型") @Data +@Builder +@AllArgsConstructor +@NoArgsConstructor public class ExtHiTaskSearchDTO { @ApiModelProperty("流程实例 ID") @@ -29,4 +37,7 @@ public class ExtHiTaskSearchDTO { @ApiModelProperty("任务状态") private BpmnProcessInstanceResultEnum status; + + @ApiModelProperty("需要排除的id列表") + private List excludeIds; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryCreateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryCreateDTO.java index a8042344c..d80e8f178 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryCreateDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryCreateDTO.java @@ -1,5 +1,6 @@ package cn.axzo.workflow.common.model.request.category; +import cn.axzo.workflow.common.enums.BusinessTypeEnum; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.hibernate.validator.constraints.Length; @@ -68,4 +69,22 @@ public class CategoryCreateDTO { @ApiModelProperty(value = "租户", example = "1") private String tenantId = ""; + /** + * 业务类型 + */ + @ApiModelProperty(value = "业务类型,SIGN-签署业务,APPROVAL-审批业务") + private BusinessTypeEnum businessType; + + /** + * 图标 + */ + @ApiModelProperty(value = "图标") + private String icon; + + /** + * 是否展示在发起工作台 + */ + @ApiModelProperty(value = "是否展示在发起工作台,默认为false") + private Boolean displayInitiateMenu = false; + } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryGroupVarSearchDto.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryGroupVarSearchDto.java new file mode 100644 index 000000000..e0862cc54 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryGroupVarSearchDto.java @@ -0,0 +1,28 @@ +package cn.axzo.workflow.common.model.request.category; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.Min; + +@ApiModel("业务分类分组和变量搜索入参模型") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CategoryGroupVarSearchDto { + + @Min(value = 1, message = "字典ID必须大于0") + @ApiModelProperty(value = "字典 ID") + private Long dictId; + + /** + * 所属业务code + */ + @ApiModelProperty(value = "所属业务") + private String category; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryGroupVarUpsertDto.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryGroupVarUpsertDto.java new file mode 100644 index 000000000..24fde2c59 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategoryGroupVarUpsertDto.java @@ -0,0 +1,104 @@ +package cn.axzo.workflow.common.model.request.category; + +import cn.axzo.workflow.common.enums.VarTypeEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.Valid; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.io.Serializable; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CategoryGroupVarUpsertDto { + + @ApiModelProperty(value = "字典id") + @Min(value = 1, message = "字典id值必须大于等1") + @NotNull(message = "字典id不能为空") + private Long dictId; + + @Valid + private List groupVos; + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class CategoryGroupUpsertVo implements Serializable { + private static final long serialVersionUID = 5406711143425155649L; + + /** + * id + */ + private Long id; + + /** + * 上级分组id + */ + private Long parentGroupId; + + /** + * 分组名 + */ + @NotBlank(message = "分组名称不能为空") + @Length(max = 50, message = "字典标签最长只支持50个字符") + private String groupName; + + @NotNull(message = "序号不能为空") + private Integer ordinal; + + @Valid + private List vars; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class CategoryVarUpsertVo implements Serializable { + + private static final long serialVersionUID = -1918203166603971593L; + + private Long id; + + /** + * 分组id + */ + private Long groupId; + + /** + * 变量类型,文本/图片 + */ + @NotNull(message = "变量类型不能为空") + private VarTypeEnum type; + + /** + * 变量code + */ + @NotBlank(message = "变量编码不能为空") + @Length(max = 50, message = "变量编码最长只支持50个字符") + @Pattern(regexp = "^[a-zA-Z]+$", message = "变量编码仅限英文字符") + private String code; + + /** + * 变量名称 + */ + @NotBlank(message = "变量名称不能为空") + @Length(max = 50, message = "变量名称最长只支持50个字符") + private String name; + + @NotNull(message = "序号不能为空") + private Integer ordinal; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategorySearchDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategorySearchDTO.java index 27dd6c794..2e2738eae 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategorySearchDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/category/CategorySearchDTO.java @@ -1,5 +1,6 @@ package cn.axzo.workflow.common.model.request.category; +import cn.axzo.workflow.common.enums.BusinessTypeEnum; import cn.axzo.workflow.common.model.request.BpmPageParam; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @@ -48,4 +49,13 @@ public class CategorySearchDTO extends BpmPageParam { */ @ApiModelProperty(value = "租户 ID", example = "1") private String tenantId = ""; + + /** + * 业务类型 + */ + @ApiModelProperty(value = "业务类型") + private BusinessTypeEnum businessType; + + @ApiModelProperty(value = "根据创建时间排序") + private String orderCreateAt; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/FormPermissionMetaInfo.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/FormPermissionMetaInfo.java index a8dc5d5b1..0a8cd715e 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/FormPermissionMetaInfo.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/FormPermissionMetaInfo.java @@ -4,7 +4,6 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; import java.io.Serializable; @@ -34,22 +33,26 @@ public class FormPermissionMetaInfo implements Serializable { /** * 可编辑必填 */ + @Builder.Default private Boolean required = false; /** * 可编辑非必填 */ + @Builder.Default private Boolean editable = false; /** * 只读 */ - private Boolean readonly = false; + @Builder.Default + private Boolean readonly = true; /** * 隐藏 */ - private Boolean hidden = true; + @Builder.Default + private Boolean hidden = false; /** * 前端回显字段,后端不做任何消费逻辑 */ @@ -58,10 +61,10 @@ public class FormPermissionMetaInfo implements Serializable { // 将对象的属性转换为对应的整数表示 public int toBinary() { int binaryValue = 0; - binaryValue |= (required? 1 : 0) << 3; - binaryValue |= (editable? 1 : 0) << 2; - binaryValue |= (readonly? 1 : 0) << 1; - binaryValue |= (hidden? 1 : 0); + binaryValue |= (required ? 1 : 0) << 3; + binaryValue |= (editable ? 1 : 0) << 2; + binaryValue |= (readonly ? 1 : 0) << 1; + binaryValue |= (hidden ? 1 : 0); return binaryValue; } @@ -73,8 +76,9 @@ public class FormPermissionMetaInfo implements Serializable { boolean hidden = (binaryValue & 1) == 1; return new FormPermissionMetaInfo(fieldId, fieldName, required, editable, readonly, hidden, null); } + public FormPermissionMetaInfo toReadonly() { - if(required || editable || readonly) { + if (required || editable || readonly) { setRequired(false); setEditable(false); setReadonly(true); diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/FormFieldDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/FormFieldDTO.java index 593e4811e..4a07d8a8c 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/FormFieldDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/FormFieldDTO.java @@ -30,6 +30,17 @@ public class FormFieldDTO { /** * 前端组件类型 + *

+ * { label: "文本", value: "input" }, + * { label: "多行文本", value: "textarea" }, + * { label: "附件", value: "upload" }, + * { label: "图片", value: "image" }, + * { label: "自定义组件", value: "customComponent" }, + * { label: "任务单", value: "taskOrder" }, + * { label: "整改单", value: "rectifyOrder" }, + * { label: "变洽签单", value: "changeSignatureOrder" }, + * { label: "通讯录", value: "contacts" }, + * { label: "金额", value: "amount" }, */ @ApiModelProperty(value = "前端的组件类型") private String type; @@ -51,7 +62,7 @@ public class FormFieldDTO { * 部分组件是多值,比如日期区间,多选, 多附件 */ @ApiModelProperty(value = "表单字段默认值", example = "account") - private List value; + private Object value; /** * 表单占位提示 diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/StartFormSearchDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/StartFormSearchDTO.java index 27a7698ff..62f4eac0e 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/StartFormSearchDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/definition/StartFormSearchDTO.java @@ -35,4 +35,7 @@ public class StartFormSearchDTO { */ @ApiModelProperty(value = "是否显示原始的默认值", hidden = true, notes = "如果为 true 则将默认值中的 ${} 变量不进行替换") private Boolean showOriginDefaultValue = false; + + @ApiModelProperty(value = "是否抛出内部异常") + private Boolean throwException = true; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/instance/FormVariablesUpdateDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/instance/FormVariablesUpdateDTO.java new file mode 100644 index 000000000..b35a1674b --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/form/instance/FormVariablesUpdateDTO.java @@ -0,0 +1,44 @@ +package cn.axzo.workflow.common.model.request.form.instance; + +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; +import java.util.Map; + +/** + * 更新指定流程表单最后一次操作的表单内容的入参模型 + * + * @author wangli + * @since 2025-02-08 10:04 + */ +@ApiModel("更新指定流程表单最后一次操作的表单内容") +@Data +public class FormVariablesUpdateDTO implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 流程实例 ID + */ + @ApiModelProperty(value = "流程实例ID") + @NotBlank(message = "流程实例 ID 不能为空") + private String processInstanceId; + + /** + *

+ * 图片类型和附件类型组件:请用 @see {@link UploadFieldDTO} 对象集合传入 + *

+     *     // form_image 为表单项的 key, value 为 UploadFileDTO 对象集合,如果前端使用了组件,一般建议回传所有属性;特殊情况下可以只传 fileUrl
+     *     "form_image": [{
+     *          "fileName": "",
+     *          "fileUrl": "http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960",
+     *          "fileKey": 123
+     *      }]
+     * 
+ */ + @ApiModelProperty(value = "通过表单创建流程时传入的初始表单数据") + private Map formVariables; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/model/BpmnModelDetailVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/model/BpmnModelDetailVO.java index ce2a09c4e..2d6df8e43 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/model/BpmnModelDetailVO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/model/BpmnModelDetailVO.java @@ -1,6 +1,7 @@ package cn.axzo.workflow.common.model.response.bpmn.model; import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonModel; +import io.swagger.annotations.Api; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -28,6 +29,30 @@ public class BpmnModelDetailVO extends BpmnModelBaseVO { @ApiModelProperty(value = "启用状态") private Integer status; + /** + * 打印开关 + */ + @ApiModelProperty(value = "打印状态") + private Integer printStatus; + + /** + * 打印文件名称 + */ + @ApiModelProperty(value = "打印文件名称") + private String printFileName; + + /** + * 打印模板名称 + */ + @ApiModelProperty(value = "打印模板名称") + private String printTemplateName; + + /** + * 打印模板配置内容 + */ + @ApiModelProperty(value = "打印模板配置内容") + private String printTemplateConfig; + /** * 描述 */ diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/model/doc/DocBaseVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/model/doc/DocBaseVO.java new file mode 100644 index 000000000..7fc298914 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/model/doc/DocBaseVO.java @@ -0,0 +1,97 @@ +package cn.axzo.workflow.common.model.response.bpmn.model.doc; + +import cn.axzo.workflow.common.enums.FileTypeEnum; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 基础的流程关联文档的响应模型 + * + * @author wangli + * @since 2025-03-31 09:49 + */ +@ApiModel("基础的流程关联文档的响应模型") +@Data +@NoArgsConstructor +public class DocBaseVO { + + private Long id; + + /** + * 模型 ID + */ + @ApiModelProperty(value = "模型 ID") + private String modelId; + + /** + * 模型 KEY + */ + @ApiModelProperty(value = "模型 KEY") + private String modelKey; + + /** + * 文档对应文件的检索信息 + * word/excel 对应 wps 的 Id, + * hp 对应是 + * pdf 对应 oss 中的 fileKey + */ + @ApiModelProperty(value = "文档对应文件检索信息") + private String fileRelationId; + + /** + * 文档名称(用于归档) + */ + @ApiModelProperty(value = "文档名称") + private String fileName; + + /** + * 文档模板名称 + */ + @ApiModelProperty(value = "模板名称") + private String templateName; + + /** + * 归档位置 + */ + private String location; + + /** + * 文档类型 + */ + @ApiModelProperty(value = "文档类型") + private FileTypeEnum fileType; + + /** + * 文档业务类型 + */ + @ApiModelProperty(value = "业务类型") + private String tag; + + /** + * 文档状态 + */ + @ApiModelProperty(value = "文档状态") + private Boolean status; + + @ApiModelProperty(value = "是否是临时文档") + private Boolean tempFile; + /** + * 文档是否必选 + */ + @ApiModelProperty(value = "是否必选") + private Boolean require; + + /** + * 排序号 + */ + @ApiModelProperty(value = "排序号") + private Integer order; + + /** + * 租户 ID + */ + @ApiModelProperty(value = "租户 ID") + private String tenantId; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnNodeConfigVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnNodeConfigVO.java new file mode 100644 index 000000000..e329c2c14 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnNodeConfigVO.java @@ -0,0 +1,88 @@ +package cn.axzo.workflow.common.model.response.bpmn.process; + +import cn.axzo.workflow.common.enums.ApprovalMethodEnum; +import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum; +import cn.axzo.workflow.common.enums.ApproverScopeEnum; +import cn.axzo.workflow.common.enums.ApproverSpecifyEnum; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignApproverLimit; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 发起流程前的节点列表中每个节点的模型配置信息 + * + * @author wangli + * @since 2025-03-26 09:40 + */ +@ApiModel("发起流程前的节点列表中每个节点的模型配置信息") +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class BpmnNodeConfigVO { + /** + * 审批方式, 审批节点与业务节点会有不同 + *

+ * human("human", "人工审批", ""), + * autoPassed("autoPassed", "自动通过", "[仅审批节点可能选该值]"), + * autoRejection("autoRejection", "自动驳回", "[仅审批节点可能选该值]"), + * nobody("nobody", "不设置审批人", "[仅业务节点可能有该值]"), + * bizSpecify("bizSpecify", "业务指定审批人", "[仅业务节点可能有该值]"), + */ + @ApiModelProperty(value = "审批方式") + private ApprovalMethodEnum approvalMethodEnum; + + /** + * 审批人所在范围 + *

+ * entWorkspace("entWorkspace", "企业工作台", "entWorkspaceProcessor"), + * govWorkspace("govWorkspace", "政务工作台", "govWorkspaceProcessor"), + * projectWorkspace("projectWorkspace", "项目工作台","projectWorkspaceProcessor"), + * preTaskUser("preTaskUser", "上节点审批人所在单位","preTaskUserProcessor"), + * preTaskSpecified("preTaskSpecified", "上节点审批人指定","preTaskUserProcessor"), + */ + @ApiModelProperty(value = "审批人所在范围") + private ApproverScopeEnum approverScopeEnum; + + /** + * 审批人指定 + *

+ * position("position", "指定岗位"), + * role("role", "指定角色"), + * identity("identity", "指定身份"), + * fixedPerson("fixedPerson", "固定人员"), 如果节点是该类型,则该节点下的人,会默认回显至该上级模型属性中 + * initiatorLeader("initiatorLeader", "发起人主管"), + * initiatorSpecified("initiatorSpecified", "发起人自选"), + */ + @ApiModelProperty(value = "审批人指定") + private ApproverSpecifyEnum approverSpecifyEnum; + + /** + * 审批人为空时的处理方式 + *

+ * autoPassed("autoPassed", "自动通过"), + * autoRejection("autoRejection", "自动驳回"), + * autoSkipped("autoSkipped", "自动跳过"), + * transferToAdmin("transferToAdmin", "转交给管理员"), + * specifyAssignee("specifyAssignee", "指定审批人"), + */ + @ApiModelProperty(value = "审批人为空时的处理方式") + private ApproverEmptyHandleTypeEnum approverEmptyHandleTypeEnum; + + /** + * 是否开启电子签名 + */ + @ApiModelProperty(value = "是否开启电子签名") + private Boolean signature; + + /** + * 签署确认节点审批人限制规则 + */ + @ApiModelProperty(value = "签署确认节点审批人限制规则") + private BpmnSignApproverLimit signApproverLimit; + +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnProcessInstanceLogVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnProcessInstanceLogVO.java index 8ddcb835b..8c9689523 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnProcessInstanceLogVO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/BpmnProcessInstanceLogVO.java @@ -154,6 +154,12 @@ public class BpmnProcessInstanceLogVO { @ApiModelProperty(value = "工作台类型") private WorkspaceType workspaceType; + /** + * 当前审批能否打印 + */ + @ApiModelProperty(value = "能否打印") + private Boolean catPrint; + @ApiModelProperty(value = "程序计算按钮使用,非对外使用", hidden = true) private transient BpmnButtonConf calculatingButtonConf; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/NodesByModelVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/NodesByModelVO.java new file mode 100644 index 000000000..7e9cdde46 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/NodesByModelVO.java @@ -0,0 +1,69 @@ +package cn.axzo.workflow.common.model.response.bpmn.process; + +import cn.axzo.workflow.common.enums.BpmnFlowNodeMode; +import cn.axzo.workflow.common.enums.BpmnFlowNodeType; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * 模型节点集合响应模型 + * + * @author wangli + * @since 2025-01-15 10:40 + */ +@ApiModel("模型节点集合响应模型") +@Data +@Accessors(chain = true) +public class NodesByModelVO { + + /** + * 节点 ID + */ + @ApiModelProperty(value = "节点 ID") + private String activityId; + + /** + * 节点名称 + */ + @ApiModelProperty(value = "节点名称") + private String activityName; + + /** + * 是否支持发起人自选人 + */ + @ApiModelProperty(value = "是否支持发起自选人") + private Boolean supportSpecify; + + /** + * 节点的人 + */ + private List assigners; + /** + * 节点模式(会、或签,普通节点) + */ + @ApiModelProperty(value = "节点模式(会、或签,普通节点)") + private BpmnFlowNodeMode nodeModel; + + /** + * 节点类型, 只要是以下类型 + *

+ * NODE_STARTER("NODE_STARTER", "发起人节点"), + * NODE_TASK("NODE_TASK", "审核节点"), + * NODE_BUSINESS("NODE_BUSINESS", "业务节点"), + * NODE_SIGN("NODE_SIGN", "签署确认节点"), + * NODE_CARBON_COPY("NODE_CARBON_COPY", "抄送节点"), + */ + @ApiModelProperty(value = "节点类型") + private BpmnFlowNodeType nodeType; + + /** + * 节点原始配置信息 + */ + @ApiModelProperty(value = "节点原始配置") + private BpmnNodeConfigVO nodeConfig; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/doc/DocPendingVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/doc/DocPendingVO.java new file mode 100644 index 000000000..78d5ac6bf --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/process/doc/DocPendingVO.java @@ -0,0 +1,47 @@ +package cn.axzo.workflow.common.model.response.bpmn.process.doc; + +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 基础的流程关联文档待办中的响应模型 + * + * @author wangli + * @since 2025-03-31 09:49 + */ +@EqualsAndHashCode(callSuper = true) +@ApiModel("基础的流程关联文档待办中的响应模型") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DocPendingVO extends DocBaseVO { + + /** + * oss 的 fileKey + */ + @ApiModelProperty(value = "oss 的 fileKey") + private String fileKey; + + /** + * 文件 OBS 地址 + */ + @ApiModelProperty(value = "文件 OBS 地址") + private String ossUrl; + + /** + * 文件大小,单位 Byte + */ + @ApiModelProperty(value = "文件大小,单位 byte") + private Long storageSize; + + /** + * 文档已读状态,true:已读 + */ + @ApiModelProperty(value = "文档已读状态,true:已读") + private Boolean readStatus; +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskButtonVo.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskButtonVo.java new file mode 100644 index 000000000..42d2f722d --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskButtonVo.java @@ -0,0 +1,60 @@ +package cn.axzo.workflow.common.model.response.bpmn.task; + +import cn.axzo.workflow.common.enums.BpmnProcessTaskResultEnum; +import cn.axzo.workflow.common.enums.ButtonVisibleScopeEnum; +import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.io.Serializable; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class BpmnTaskButtonVo implements Serializable { + + private static final long serialVersionUID = -2701890396246979758L; + + /** + * 执行人任务状态 + */ + @ApiModelProperty(value = "执行人任务执行状态") + private BpmnProcessTaskResultEnum executorTaskResult; + + /** + * 发起人执行状态 + */ + @ApiModelProperty(value = "发起人任务执行状态") + private BpmnProcessTaskResultEnum initiatorTaskResult; + + /** + * 当前任务执行人以及发起人全量按钮 + */ + @ApiModelProperty(value = "当前任务相关以及发起人的全量按钮") + private List buttons; + + /** + * 需要隐藏的按钮 + */ + @ApiModelProperty(value = "需要隐藏的自定义按钮") + private List customHiddenButtons; + + /** + * 全量的配置按钮 + */ + @ApiModelProperty(value = "全量的配置按钮") + private List allConfigButtons; + + @Data + @EqualsAndHashCode(callSuper = true) + @AllArgsConstructor + @NoArgsConstructor + public static final class BpmnButtonMetaInfoWithVisibleScope extends BpmnButtonMetaInfo { + + private static final long serialVersionUID = 5758633314769798044L; + + private List visibleScopes; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskInstanceLogVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskInstanceLogVO.java index 2c191b8d4..6671f6e11 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskInstanceLogVO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/bpmn/task/BpmnTaskInstanceLogVO.java @@ -107,6 +107,12 @@ public class BpmnTaskInstanceLogVO { */ @ApiModelProperty(value = "手写签名地址") private String signatureUrl; + + /** + * 电子签名开关 + */ + @ApiModelProperty(value = "电子签名开关") + private Boolean signature; /** * 审批人快照信息 */ diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/category/CategoryGroupVarItemVo.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/category/CategoryGroupVarItemVo.java new file mode 100644 index 000000000..128c52c51 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/category/CategoryGroupVarItemVo.java @@ -0,0 +1,75 @@ +package cn.axzo.workflow.common.model.response.category; + + +import cn.axzo.workflow.common.enums.VarTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class CategoryGroupVarItemVo implements Serializable { + + private static final long serialVersionUID = -3349551294678140220L; + + /** + * 分组id + */ + private Long id; + + /** + * 上级分组id + */ + private Long parentGroupId; + + /** + * 分组名 + */ + private String groupName; + + /** + * 当前分组对应的变量 + */ + private List vars; + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class CategoryVarItemVo implements Serializable { + + private static final long serialVersionUID = -1918203166603971593L; + + /** + * 变量id + */ + private Long id; + + /** + * 分组id + */ + private Long groupId; + + /** + * 变量类型,文本/图片 + */ + private VarTypeEnum type; + + /** + * 变量code + */ + private String code; + + /** + * 变量名称 + */ + private String name; + + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/category/CategoryItemVO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/category/CategoryItemVO.java index b106432aa..63c39692c 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/category/CategoryItemVO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/category/CategoryItemVO.java @@ -1,5 +1,6 @@ package cn.axzo.workflow.common.model.response.category; +import cn.axzo.workflow.common.enums.BusinessTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -34,6 +35,15 @@ public class CategoryItemVO { @ApiModelProperty(value = "工作台类型值") private String workspaceTypeCode; + @ApiModelProperty(value = "图标") + private String icon; + + @ApiModelProperty(value = "业务类型") + private BusinessTypeEnum businessType; + + @ApiModelProperty(value = "是否展示在发起工作台") + private Boolean displayInitiateMenu; + @ApiModelProperty(value = "更新时间") private Date updateAt; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/MessagePushDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/MessagePushDTO.java index 04a5e0d79..184614bf7 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/MessagePushDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/MessagePushDTO.java @@ -31,6 +31,11 @@ public class MessagePushDTO implements Serializable { */ private String processInstanceId; + /** + * 流程归属的租户 ID + */ + private String adscriptionTenantId; + /** * 业务 ID */ @@ -66,4 +71,13 @@ public class MessagePushDTO implements Serializable { */ private BpmnApproveConf processApproveConf; + /** + * 节点电子签开关 + */ + private Boolean activitySignature; + /** + * 催办专用属性,催办时的终端类型(管理端、工人端) + */ + private String terminalType; + } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessInstanceDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessInstanceDTO.java index 4911cfbe7..b36007eca 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessInstanceDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessInstanceDTO.java @@ -2,6 +2,7 @@ package cn.axzo.workflow.common.model.response.mq; import cn.axzo.workflow.common.enums.ProcessInstanceEventEnum; import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignConf; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import lombok.Data; import lombok.experimental.Accessors; @@ -85,9 +86,14 @@ public class ProcessInstanceDTO implements Serializable { */ private BpmnTaskDelegateAssigner initiator; + /** + * 关闭审批实例前最后一个操作人 + */ + private BpmnTaskDelegateAssigner lastOperationAssigner; + /** * 对流程实例做否定操作时的原因 - * + *

* 如: 中止/驳回/撤回功能 */ private String reason; @@ -97,6 +103,11 @@ public class ProcessInstanceDTO implements Serializable { */ private BpmnNoticeConf noticeConf; + /** + * 模型中的签署配置 + */ + private BpmnSignConf signConf; + /** * 当前数据的流程引擎版本 */ diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java index 2822419c5..d356f394a 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/mq/ProcessTaskDTO.java @@ -94,6 +94,16 @@ public class ProcessTaskDTO implements Serializable { */ private BpmnTaskDelegateAssigner approver; + /** + * 被转交人信息 + */ + private BpmnTaskDelegateAssigner transferTargetApprover; + + /** + * 转交意见 + */ + private String transferAdvice; + /** * 通知方式 */ diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/print/PrintModelDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/print/PrintModelDTO.java new file mode 100644 index 000000000..6bdbc1f2e --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/response/print/PrintModelDTO.java @@ -0,0 +1,38 @@ +package cn.axzo.workflow.common.model.response.print; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 打印模板响应模型 + * + * @author wangli + * @since 2025-02-11 10:41 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class PrintModelDTO { + + /** + * 打印文件名称 + */ + @ApiModelProperty(value = "打印文件名称") + private String printFileName; + + /** + * 打印模板名称 + */ + @ApiModelProperty(value = "打印模板名称") + private String printTemplateName; + + /** + * 打印模板内容(前端打印组件的生成的完全数据) + */ + @ApiModelProperty(value = "打印模板内容") + private String printTemplateConfig; +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/CommonContext.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/CommonContext.java index 49cba93f3..02eeb6713 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/CommonContext.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/CommonContext.java @@ -26,6 +26,7 @@ public abstract class CommonContext implements OperationContext { private BpmnProcessInstanceVO instanceVO; private BpmnProcessDefinitionVO definitionVO; private String processInstanceVersion; + private BpmnTaskDelegateAssigner lastOperationAssigner; public ProcessInstance getProcessInstance(Supplier supplier) { if (Objects.isNull(processInstance)) { @@ -69,6 +70,13 @@ public abstract class CommonContext implements OperationContext { return initiator; } + public BpmnTaskDelegateAssigner getLastOperationAssigner(Supplier supplier) { + if (Objects.isNull(lastOperationAssigner)) { + lastOperationAssigner = supplier.get(); + } + return lastOperationAssigner; + } + public String getProcessInstanceVersion(Supplier supplier) { if (!StringUtils.hasText(processInstanceVersion)) { processInstanceVersion = supplier.get(); @@ -77,10 +85,12 @@ public abstract class CommonContext implements OperationContext { } @Override + @SuppressWarnings("unchecked") public void setContext(OperationContext context) { this.context = (T) context; } + @SuppressWarnings("unchecked") @Override public T getContext() { return context; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/ProcessOperationContext.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/ProcessOperationContext.java index cb06e6e89..9342cd93b 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/ProcessOperationContext.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/context/ProcessOperationContext.java @@ -1,5 +1,12 @@ package cn.axzo.workflow.core.common.context; +import org.flowable.engine.ProcessEngineConfiguration; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + /** * 流程相关操作上下文 * @@ -8,5 +15,20 @@ package cn.axzo.workflow.core.common.context; */ public class ProcessOperationContext extends CommonContext { + private List processFileTags; + private ProcessEngineConfiguration processEngineConfiguration; + public List getProcessFileTags(Supplier> supplier) { + if (CollectionUtils.isEmpty(processFileTags)) { + processFileTags = supplier.get(); + } + return processFileTags; + } + + public ProcessEngineConfiguration getProcessEngineConfiguration(Supplier supplier) { + if (Objects.isNull(processEngineConfiguration)) { + return supplier.get(); + } + return processEngineConfiguration; + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java index 23eadcd0f..06400d673 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnProcessTaskResultEnum.java @@ -9,6 +9,7 @@ import java.util.Arrays; * @since 2023/9/18 17:11 */ public enum BpmnProcessTaskResultEnum { + HANDLING("HANDLING", "审批中"), // 修改这里 PENDING("PENDING", "待处理"), PROCESSED("PROCESSED", "已处理"), AUTO_SKIP("AUTO_SKIP", "任务自动跳过"), diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/event/ApiLogListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/event/ApiLogListener.java index f446ef005..1d98e44cb 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/event/ApiLogListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/event/ApiLogListener.java @@ -5,15 +5,12 @@ import cn.axzo.workflow.core.repository.entity.ExtAxApiLog; import cn.axzo.workflow.core.service.ExtAxApiLogService; import cn.hutool.json.JSONUtil; import com.google.common.base.Strings; -import com.google.common.collect.Lists; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; -import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java index 10a6f22e9..d5fc06b04 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java @@ -1,6 +1,7 @@ package cn.axzo.workflow.core.common.utils; import cn.axzo.workflow.common.enums.ApprovalMethodEnum; +import cn.axzo.workflow.common.enums.ModelBizTypeEnum; import cn.axzo.workflow.common.exception.WorkflowEngineException; import cn.axzo.workflow.common.model.request.BpmnApproveConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf; @@ -9,8 +10,9 @@ import cn.axzo.workflow.common.model.request.bpmn.BpmnFieldConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonModel; import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonNode; import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignApproverLimit; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignConf; import cn.axzo.workflow.common.model.request.bpmn.model.BpmnModelCreateDTO; -import cn.axzo.workflow.common.model.request.form.FormPermissionMetaInfo; import cn.axzo.workflow.core.converter.json.AbstractBpmnJsonConverter; import cn.axzo.workflow.core.converter.json.BoundaryEventJsonConverter; import cn.axzo.workflow.core.converter.json.EndEventJsonConverter; @@ -86,6 +88,8 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_FIELD; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_FIELD_META; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_FIELD_OPTION; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_NOTICE; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_SIGN; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_SIGN_TYPE; import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_CHECKED; import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_CODE; import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_DISABLED; @@ -95,6 +99,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_O import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_TYPE; import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_VALUE; import static cn.axzo.workflow.common.constant.BpmnConstants.END_EVENT_ID; +import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_MODEL_BIZ_TYPE; import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_NODE_JSON; import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION; import static cn.axzo.workflow.common.constant.BpmnConstants.SEQUENCE_FLOW_ID; @@ -109,6 +114,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_NOTICE_MES import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_NOTICE_MESSAGE_DESTINATION_ROLES; import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_NOTICE_MESSAGE_EVENTS; import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_PENDING_MESSAGE_ID; +import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_SIGN_PENDING_MESSAGE_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_SMS_MESSAGE_ID; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CONDITION; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY; @@ -117,6 +123,8 @@ import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getButtonC import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getFieldConfig; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNoticeConfig; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getProcessApproveConf; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getSignApproverLimit; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getSignConfig; /** * BPMN json 格式转换工具 @@ -165,6 +173,7 @@ public final class BpmnJsonConverterUtil { getNoticeConfig(mainProcess).ifPresent(model::setNoticeConf); getButtonConfig(mainProcess).ifPresent(model::setButtonConf); getFieldConfig(mainProcess).ifPresent(model::setFieldConf); + getSignConfig(mainProcess).ifPresent(model::setSignConf); return model; } @@ -178,9 +187,11 @@ public final class BpmnJsonConverterUtil { * @param fieldConf * @return {@link BpmnModel} */ - public static BpmnModel convertToBpmn(BpmnJsonNode bpmnJsonNode, String id, String name, String formKey, + public static BpmnModel convertToBpmn(BpmnJsonNode bpmnJsonNode, ModelBizTypeEnum modelBizType, + String id, String name, String formKey, String documentation, BpmnApproveConf approveConf, + BpmnSignConf signConf, BpmnNoticeConf noticeConf, BpmnButtonConf buttonConf, List fieldConf, @@ -190,7 +201,9 @@ public final class BpmnJsonConverterUtil { } // 提交的 BpmnJsonNode 全部都是任务是可执行节点, 这里的转换是全局都会增加开始和结束节点. 因为前段的提交的数据是不包含开始和结束 BpmnModel bpmnModel = new BpmnModel(); - + ExtensionAttribute modelType = new ExtensionAttribute(); + modelType.setName(FLOW_MODEL_BIZ_TYPE); + modelType.setValue(Objects.nonNull(modelBizType) ? modelBizType.getType() : ModelBizTypeEnum.FLOWABLE.getType()); ExtensionAttribute serverVersion = new ExtensionAttribute(); serverVersion.setName(FLOW_SERVER_VERSION); serverVersion.setValue(serverVersionStr); @@ -202,11 +215,14 @@ public final class BpmnJsonConverterUtil { mainProcess.setId(id); mainProcess.setName(name); mainProcess.setDocumentation(documentation); + mainProcess.addAttribute(modelType); mainProcess.addAttribute(serverVersion); mainProcess.addAttribute(jsonMetaValue); //设置流程审批相关高级配置 setProcessApproveConfigs(approveConf, mainProcess); + // 设置签署配置 + setSignConfig(signConf, mainProcess); // 设置流程的通知管理配置 setProcessNoticeConfig(noticeConf, mainProcess); // 设置流程的默认的按钮配置 @@ -223,7 +239,10 @@ public final class BpmnJsonConverterUtil { mainProcess.addFlowElement(convertJsonToElement(EndEvent.class, mainProcess, formKey)); // 解析前端传入的模型设计 json - List lastNodeIds = create(formKey, bpmnJsonNode, mainProcess, bpmnModel, null, START_EVENT_ID); + List lastNodeIds = Lists.newArrayList(START_EVENT_ID); + if (StringUtils.hasText(bpmnJsonNode.getId())) { + lastNodeIds = create(formKey, bpmnJsonNode, mainProcess, bpmnModel, null, START_EVENT_ID); + } if (CollectionUtils.isEmpty(lastNodeIds)) { throw new WorkflowEngineException(CONVERTOR_COMMON_ERROR, "未找到链接结束节点的节点数据"); @@ -240,6 +259,39 @@ public final class BpmnJsonConverterUtil { return bpmnModel; } + private static void setSignConfig(BpmnSignConf signConf, Process mainProcess) { + if (Objects.isNull(signConf)) { + return; + } + ExtensionElement signConfigElement = new ExtensionElement(); + signConfigElement.setName(CONFIG_SIGN); + + if (Objects.nonNull(signConf.getSignType())) { + ExtensionElement signType = new ExtensionElement(); + signType.setName(CONFIG_SIGN_TYPE); + ExtensionAttribute signTypeAttr = new ExtensionAttribute(); + signTypeAttr.setName(ELEMENT_ATTRIBUTE_TYPE); + signTypeAttr.setValue(signConf.getSignType().getType()); + signType.addAttribute(signTypeAttr); + signType.setElementText(signConf.getSignType().getDesc()); + signConfigElement.addChildElement(signType); + } + + // 签署待办消息模板配置 + if (Objects.nonNull(signConf.getSignPendingProperty()) && Objects.nonNull(signConf.getSignPendingProperty().getPendingMessageId())) { + ExtensionElement signPendingMessage = new ExtensionElement(); + signPendingMessage.setName(TEMPLATE_SIGN_PENDING_MESSAGE_ID); + ExtensionAttribute pendingMessageAttribute = new ExtensionAttribute(); + pendingMessageAttribute.setName(ELEMENT_ATTRIBUTE_VALUE); + pendingMessageAttribute.setValue(signConf.getSignPendingProperty().getPendingMessageId()); + signPendingMessage.addAttribute(pendingMessageAttribute); + signPendingMessage.setElementText(StringUtils.hasText(signConf.getSignPendingProperty().getViewJson()) ? + signConf.getSignPendingProperty().getViewJson() : ""); + signConfigElement.addChildElement(signPendingMessage); + } + mainProcess.addExtensionElement(signConfigElement); + } + private static void setProcessFieldConfig(List fieldConf, Process mainProcess) { if (CollectionUtils.isEmpty(fieldConf)) { return; @@ -692,6 +744,10 @@ public final class BpmnJsonConverterUtil { clz = UserTask.class; } break; + case NODE_SIGN: + // 签署确认节点 + clz = UserTask.class; + break; case NODE_TRIGGER: // 这个类型目前暂不支持 case NODE_CARBON_COPY: @@ -710,7 +766,7 @@ public final class BpmnJsonConverterUtil { } private static FlowElement convertJsonToElement(Class clz, BpmnJsonNode bpmnJsonNode, - Process process, String formKey) { + Process process, String formKey) { AbstractBpmnJsonConverter converter = CONVERTERS.getOrDefault(clz, new NotSupportConverter()); FlowElement flowElement = converter.convertJsonToElement(bpmnJsonNode, process, formKey); if (Objects.nonNull(bpmnJsonNode)) { @@ -754,19 +810,19 @@ public final class BpmnJsonConverterUtil { String content = new String(bytes, StandardCharsets.UTF_8); BpmnModelCreateDTO model = JSON.parseObject(content, BpmnModelCreateDTO.class); - BpmnModel bpmnModel = convertToBpmn(model.getJsonModel().getNode(), "id", "测试", "test-form", "remark", + BpmnModel bpmnModel = convertToBpmn(model.getJsonModel().getNode(), ModelBizTypeEnum.SIGN, "id", "测试", "test-form", "remark", model.getJsonModel().getApproveConf(), + model.getJsonModel().getSignConf(), model.getJsonModel().getNoticeConf(), model.getJsonModel().getButtonConf(), model.getJsonModel().getFieldConf(), "1.3.1-SNAPSHOT"); getNoticeConfig(bpmnModel.getMainProcess()); + getSignConfig(bpmnModel.getMainProcess()); BpmnJsonModel bpmnJsonModel = convertToJson(bpmnModel); -// FlowElement flowElement = bpmnModel.getFlowElement("node_350687681316"); - FlowElement flowElement = bpmnModel.getFlowElement("node_429076682717"); - Optional> formFieldPermissionConf - = BpmnMetaParserHelper.getFormFieldPermissionConf(flowElement); + FlowElement flowElement = bpmnModel.getFlowElement("node_350811186561"); + Optional signApproverLimit = getSignApproverLimit(flowElement); // ServiceTask serviceTask = (ServiceTask) bpmnModel.getFlowElement("node_946990365785"); // Optional> carbonCopyConfigs = BpmnMetaParserHelper.getCarbonCopyConfigs // (serviceTask); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java index cf03ea19f..379805f64 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java @@ -7,7 +7,10 @@ import cn.axzo.workflow.common.enums.ApproverSpecifyEnum; import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum; import cn.axzo.workflow.common.enums.BpmnFlowNodeType; import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; +import cn.axzo.workflow.common.enums.BpmnSignType; import cn.axzo.workflow.common.enums.CarbonCopyObjectType; +import cn.axzo.workflow.common.enums.SignApproverOrgLimitEnum; +import cn.axzo.workflow.common.enums.SignApproverRoleLimitEnum; import cn.axzo.workflow.common.model.request.BpmnApproveConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo; @@ -19,6 +22,9 @@ import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeProperty; import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeReceiver; import cn.axzo.workflow.common.model.request.bpmn.BpmnPendingProperty; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignApproverLimit; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignConf; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignPendingProperty; import cn.axzo.workflow.common.model.request.bpmn.BpmnSmsProperty; import cn.axzo.workflow.common.model.request.form.FormPermissionMetaInfo; import com.alibaba.fastjson.JSON; @@ -50,6 +56,7 @@ import java.util.stream.Collectors; import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVE_SUPPORT_BATCH_OPERATION; import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVE_USER_AGREE_SIGNATURE; import static cn.axzo.workflow.common.constant.BpmnConstants.AUTO_APPROVAL_TYPE; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_ACTIVITY_SIGNATURE; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVAL_METHOD; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVE; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVER_EMPTY_HANDLE_TYPE; @@ -71,6 +78,11 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_FIELD_OPTION import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_FIELD_PERMISSION; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_NODE_TYPE; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_NOTICE; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_SIGN; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_SIGN_APPROVER_LIMIT; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_SIGN_APPROVER_ORG_LIMIT; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_SIGN_APPROVER_ROLE_LIMIT; +import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_SIGN_TYPE; import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_CHECKED; import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_CODE; import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_DISABLED; @@ -91,6 +103,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_NOTICE_MES import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_NOTICE_MESSAGE_DESTINATION_ROLES; import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_NOTICE_MESSAGE_EVENTS; import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_PENDING_MESSAGE_ID; +import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_SIGN_PENDING_MESSAGE_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_SMS_MESSAGE_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.USER_AGREE_SIGNATURE_DEFAULT_VALUE; @@ -133,6 +146,41 @@ public final class BpmnMetaParserHelper { return Optional.of(conf); } + public static Optional getSignConfig(Process process) { + List elements = process.getExtensionElements().getOrDefault(CONFIG_SIGN, Collections.emptyList()); + if (CollectionUtils.isEmpty(elements)) { + return Optional.empty(); + } + BpmnSignConf conf = new BpmnSignConf(); + elements.get(0).getChildElements().forEach((k, v) -> { + if (CONFIG_SIGN_TYPE.equals(k)) { + conf.setSignType(BpmnSignType.valueOfType(v.get(0).getAttributeValue(null, ELEMENT_ATTRIBUTE_TYPE))); + } else if (TEMPLATE_SIGN_PENDING_MESSAGE_ID.equals(k)) { + BpmnSignPendingProperty sign = new BpmnSignPendingProperty(); + sign.setPendingMessageId(v.get(0).getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE)); + sign.setViewJson(v.get(0).getElementText()); + conf.setSignPendingProperty(sign); + } + }); + return Optional.of(conf); + } + + public static Optional getSignApproverLimit(FlowElement flowElement) { + if (Objects.nonNull(flowElement) && flowElement instanceof UserTask) { + return getSignApproverLimit((UserTask) flowElement); + } + return Optional.empty(); + } + + public static Optional getSignApproverLimit(UserTask userTask) { + return defaultValid(userTask, CONFIG_SIGN_APPROVER_LIMIT).map(element -> { + BpmnSignApproverLimit signApproverLimit = new BpmnSignApproverLimit(); + signApproverLimit.setOrgLimit(SignApproverOrgLimitEnum.valueOfType(element.getChildElements().get(CONFIG_SIGN_APPROVER_ORG_LIMIT).get(0).getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE))); + signApproverLimit.setRoleLimit(SignApproverRoleLimitEnum.valueOfType(element.getChildElements().get(CONFIG_SIGN_APPROVER_ROLE_LIMIT).get(0).getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE))); + return signApproverLimit; + }); + } + /** * 获取流程模型全局兜底的消息模板相关配置 * @@ -372,18 +420,46 @@ public final class BpmnMetaParserHelper { return defaultValid(flowElement, CONFIG_APPROVAL_METHOD).map(element -> ApprovalMethodEnum.valueOf(element.getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE))); } + public static Optional getApproverScope(FlowElement flowElement) { + if (Objects.nonNull(flowElement) && flowElement instanceof UserTask) { + return getApproverScope((UserTask) flowElement); + } + return Optional.empty(); + } + public static Optional getApproverScope(UserTask userTask) { return defaultValid(userTask, CONFIG_APPROVER_SCOPE).map(element -> ApproverScopeEnum.valueOf(element.getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE))); } + public static Optional getApproverSpecify(FlowElement flowElement) { + if (Objects.nonNull(flowElement) && flowElement instanceof UserTask) { + return getApproverSpecify((UserTask) flowElement); + } + return Optional.empty(); + } + public static Optional getApproverSpecify(UserTask userTask) { return defaultValid(userTask, CONFIG_APPROVER_SPECIFY).map(element -> ApproverSpecifyEnum.valueOf(element.getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE))); } + public static Optional getApproverSpecifyValue(FlowElement flowElement) { + if (Objects.nonNull(flowElement) && flowElement instanceof UserTask) { + return getApproverSpecifyValue((UserTask) flowElement); + } + return Optional.empty(); + } + public static Optional getApproverSpecifyValue(UserTask userTask) { return defaultValid(userTask, CONFIG_APPROVER_SPECIFY).map(element -> StringUtils.hasLength(element.getElementText()) ? element.getElementText() : "[]"); } + public static Optional getApproverEmptyHandleType(FlowElement flowElement) { + if (Objects.nonNull(flowElement) && flowElement instanceof UserTask) { + return getApproverEmptyHandleType((UserTask) flowElement); + } + return Optional.empty(); + } + public static Optional getApproverEmptyHandleType(UserTask userTask) { return defaultValid(userTask, CONFIG_APPROVER_EMPTY_HANDLE_TYPE).map(element -> ApproverEmptyHandleTypeEnum.valueOf(element.getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE))); } @@ -392,6 +468,18 @@ public final class BpmnMetaParserHelper { return defaultValid(userTask, CONFIG_APPROVER_EMPTY_HANDLE_TYPE).map(element -> StringUtils.hasLength(element.getElementText()) ? element.getElementText() : "[]"); } + public static Boolean getActivitySignature(FlowElement flowElement) { + return defaultValid(flowElement, CONFIG_ACTIVITY_SIGNATURE).map(element -> Boolean.valueOf(element.getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE))).orElse(false); + } + + public static Boolean getSupportInitiatorSpecified(FlowElement flowElement) { + ApprovalMethodEnum approvalMethodEnum = getApprovalMethod(flowElement).orElse(null); + ApproverSpecifyEnum specifyEnum = getApproverSpecify(flowElement).orElse(null); + return Objects.nonNull(specifyEnum) + && Objects.equals(approvalMethodEnum, ApprovalMethodEnum.human) + && Objects.equals(specifyEnum, ApproverSpecifyEnum.initiatorSpecified); + } + private static Optional defaultValid(FlowElement flowElement, String elementName) { if (Objects.isNull(flowElement)) { return Optional.empty(); @@ -511,7 +599,6 @@ public final class BpmnMetaParserHelper { return fields; } - public static void main(String[] args) { List conf1 = genericConf(5); conf1.forEach(e -> System.out.println("conf1 ---> e.fieldId = " + e.getFieldId() + " ,e.getFieldName() = " + e.getFieldName() + " ,e.toBinary() = " + e.toBinary() + " ,e.toBinaryString() = " + e.toBinaryString())); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/FormHelper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/FormHelper.java index 9c9017dd3..26d2bc007 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/FormHelper.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/FormHelper.java @@ -68,7 +68,7 @@ public class FormHelper { } oldParams.put(FIELD_PROPERTY_DEFAULT_VALUE, defaultValue); if (injectDefaultValue(formField) && StringUtils.hasText(defaultValue)) { - formField.setValue(Lists.newArrayList(defaultValue)); + formField.setValue(defaultValue); } } formField.setParams(oldParams); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/CustomEventManager.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/CustomEventManager.java new file mode 100644 index 000000000..141dbc520 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/CustomEventManager.java @@ -0,0 +1,49 @@ +package cn.axzo.workflow.core.conf; + +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.stereotype.Component; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +/** + * Spring event 优雅关闭处理器 + * + * @author wangli + * @since 2025-04-07 17:10 + */ +@Component +public class CustomEventManager implements ApplicationEventPublisherAware { + + private ApplicationEventPublisher publisher; + private final BlockingQueue eventQueue = new LinkedBlockingQueue<>(); + private int eventCount = 0; + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + public synchronized void publishEvent(ApplicationEvent event) { + eventQueue.add(event); + eventCount++; + publisher.publishEvent(event); + } + + public synchronized void eventProcessed() { + eventQueue.poll(); + eventCount--; + } + + public synchronized boolean isAllEventsProcessed() { + return eventCount == 0; + } + + public synchronized void waitForEventsToBeProcessed() throws InterruptedException { + while (!isAllEventsProcessed()) { + Thread.sleep(100); + } + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/CustomJobServiceConfiguration.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/CustomJobServiceConfiguration.java new file mode 100644 index 000000000..3aae96886 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/CustomJobServiceConfiguration.java @@ -0,0 +1,37 @@ +package cn.axzo.workflow.core.conf; + +import cn.axzo.workflow.core.engine.job.service.CustomTimerJobEntityManagerImpl; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.impl.AbstractEngineConfiguration; +import org.flowable.common.engine.impl.EngineConfigurator; +import org.flowable.job.service.JobServiceConfiguration; +import org.flowable.spring.SpringProcessEngineConfiguration; + +/** + * 自定义的任务服务的配置类 + * + * @author wangli + * @since 2025-03-12 14:18 + */ +@Slf4j +public class CustomJobServiceConfiguration implements EngineConfigurator { + + @Override + public void beforeInit(AbstractEngineConfiguration engineConfiguration) { + + } + + @Override + public void configure(AbstractEngineConfiguration engineConfiguration) { + if (!(engineConfiguration instanceof SpringProcessEngineConfiguration)) { + return; + } + JobServiceConfiguration jobServiceConfiguration = ((SpringProcessEngineConfiguration) engineConfiguration).getJobServiceConfiguration(); + jobServiceConfiguration.setTimerJobEntityManager(new CustomTimerJobEntityManagerImpl(jobServiceConfiguration, jobServiceConfiguration.getTimerJobDataManager())); + } + + @Override + public int getPriority() { + return 0; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java index 17bb138fa..46cbff211 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java @@ -3,6 +3,7 @@ package cn.axzo.workflow.core.conf; import cn.axzo.workflow.core.common.utils.SpringContextUtils; import cn.axzo.workflow.core.engine.behavior.CustomActivityBehaviorFactory; import cn.axzo.workflow.core.engine.cmd.CustomCommandContextFactory; +import cn.axzo.workflow.core.engine.formhandler.CustomFormFieldHandler; import cn.axzo.workflow.core.engine.id.BasedNacosSnowflakeIdGenerator; import cn.axzo.workflow.core.engine.interceptor.CustomRetryInterceptor; import cn.axzo.workflow.core.engine.job.AsyncAbortProcessInstanceJobHandler; @@ -17,6 +18,7 @@ import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceJobHandler; import cn.axzo.workflow.core.engine.job.AsyncCountersignUserTaskJobHandler; import cn.axzo.workflow.core.engine.job.AsyncExtTaskInstJobHandler; import cn.axzo.workflow.core.engine.job.AsyncRejectTaskJobHandler; +import cn.axzo.workflow.core.engine.job.AsyncRemindTaskJobHandler; import cn.axzo.workflow.core.engine.job.AsyncTermNodeAlterJobHandler; import cn.axzo.workflow.core.engine.job.AsyncTransferUserTaskJobHandler; import cn.axzo.workflow.core.engine.job.exception.handle.CustomAsyncJobLogClearTraceExceptionHandler; @@ -36,7 +38,6 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.flowable.common.engine.api.delegate.event.FlowableEventListener; import org.flowable.common.engine.impl.history.HistoryLevel; -import org.flowable.form.engine.configurator.FormEngineConfigurator; import org.flowable.job.service.JobProcessor; import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.spring.boot.EngineConfigurationConfigurer; @@ -68,6 +69,7 @@ import static org.flowable.common.engine.impl.AbstractEngineConfiguration.DB_SCH * @author wangli * @since 2023/7/13 11:18 */ +@Slf4j @Configuration public class FlowableConfiguration { @@ -115,6 +117,7 @@ public class FlowableConfiguration { configuration.addCustomJobHandler(new AsyncActivityLeaveJobHandler(bpmnProcessActivityService)); configuration.addCustomJobHandler(new AsyncActivityCallbackJobHandler()); configuration.addCustomJobHandler(new AsyncApproveTaskWithFormJobHandler()); + configuration.addCustomJobHandler(new AsyncRemindTaskJobHandler(refreshProperties)); configurers.forEach(i -> configuration.addCustomJobHandler(i.getJobHandler())); // 异步任务异常重试时间间隔 configuration.setDefaultFailedJobWaitTime(30); @@ -132,6 +135,10 @@ public class FlowableConfiguration { configuration.setCustomPreCommandInterceptors(Lists.newArrayList( new CustomRetryInterceptor() )); + // form configuration + configuration.setFormFieldValidationEnabled(true); + configuration.setFormFieldHandler(new CustomFormFieldHandler()); + configuration.setConfigurators(Lists.newArrayList(new CustomJobServiceConfiguration())); }; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java index 58f1a6a43..3cd9f1903 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/SupportRefreshProperties.java @@ -1,6 +1,5 @@ package cn.axzo.workflow.core.conf; -import com.alibaba.nacos.api.config.annotation.NacosValue; import lombok.Data; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; @@ -44,31 +43,41 @@ public class SupportRefreshProperties { @Value(value = "${workflow.alter.enable:false}") private Boolean alterEnable; /** - * 节点卡住多久才告警 + * 节点卡住多久才告警,单位分钟 */ - @Value("${workflow.alter.delay:10}") + @Value("${workflow.alter.pauseDelay:30}") private Integer pauseDelay; /** - * 业务节点暂停告警的次数,该值不建议改小 + * 业务节点暂停告警的次数, 0代表无限制 */ - @Value(value = "${workflow.alter.retries:1000}") + @Value(value = "${workflow.alter.retries:1}") private Integer alterRetries; /** - * 业务节点暂停告警次数间的间隔 + * 业务节点任务执行间隔 */ - @Value(value = "${workflow.alter.interval:10}") + @Value(value = "${workflow.alter.interval:60}") private Integer alterInterval; /** - * 业务节点暂停告警次数间隔的时间单位 + * 业务节点任务执行间隔的时间单位 + * SECONDS/HOURS/MINUTES 支持的几种单位 */ - @Value(value = "${workflow.alter.intervalUnit:minutes}") + @Value(value = "${workflow.alter.intervalUnit:seconds}") private TimeUnit alterIntervalUnit; @Value(value = "${workflow.alter.mobiles:}") private List alterMobiles; + /** + * 是否允许重复告警 + */ + @Value(value = "${workflow.alter.repeat:false}") + private Boolean repeatAlter; + + @Value(value = "${workflow.alter.sendDingTalk:true}") + private Boolean alterSendDingTalk; + /** * 用于控制转交管理员的 API */ @@ -80,4 +89,7 @@ public class SupportRefreshProperties { @Value("${workflow.esSyncBatchSize:10}") private Integer esSyncBatchSize; + + @Value("${workflow.imTemplateCode:}") + private String imTemplateCode; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ButtonConfTypeHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ButtonConfTypeHandler.java index c235e42c9..f411169db 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ButtonConfTypeHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ButtonConfTypeHandler.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.JdbcType; @@ -26,6 +27,10 @@ import java.util.List; public class ButtonConfTypeHandler extends AbstractJsonTypeHandler { private static ObjectMapper objectMapper = new ObjectMapper(); + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + public ButtonConfTypeHandler(Class type) { if (log.isTraceEnabled()) { log.trace("JacksonTypeHandler(" + type + ")"); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListAssigneeTypeHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListAssigneeTypeHandler.java index cd54a7f40..587ca9a57 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListAssigneeTypeHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListAssigneeTypeHandler.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.JdbcType; @@ -26,6 +27,10 @@ import java.util.List; public class ListAssigneeTypeHandler extends AbstractJsonTypeHandler> { private static ObjectMapper objectMapper = new ObjectMapper(); + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + public ListAssigneeTypeHandler(Class type) { if (log.isTraceEnabled()) { log.trace("JacksonTypeHandler(" + type + ")"); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListFormFieldPermissionTypeHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListFormFieldPermissionTypeHandler.java index 705f30244..476b96057 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListFormFieldPermissionTypeHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListFormFieldPermissionTypeHandler.java @@ -1,11 +1,11 @@ package cn.axzo.workflow.core.conf.handler; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.request.form.FormPermissionMetaInfo; import com.baomidou.mybatisplus.core.toolkit.Assert; import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.JdbcType; @@ -27,6 +27,10 @@ import java.util.List; public class ListFormFieldPermissionTypeHandler extends AbstractJsonTypeHandler> { private static ObjectMapper objectMapper = new ObjectMapper(); + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + public ListFormFieldPermissionTypeHandler(Class type) { if (log.isTraceEnabled()) { log.trace("JacksonTypeHandler(" + type + ")"); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListSignFileDTOHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListSignFileDTOHandler.java new file mode 100644 index 000000000..92455d355 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListSignFileDTOHandler.java @@ -0,0 +1,64 @@ +package cn.axzo.workflow.core.conf.handler; + +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import com.baomidou.mybatisplus.core.toolkit.Assert; +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.io.IOException; +import java.util.List; + +/** + * 签署业务文件数据映射转换 + * + * @author wangli + * @since 2025-04-03 11:24 + */ +@Slf4j +@MappedTypes({List.class}) +@MappedJdbcTypes({JdbcType.VARCHAR}) +public class ListSignFileDTOHandler extends AbstractJsonTypeHandler> { + private static ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public ListSignFileDTOHandler(Class type) { + if (log.isTraceEnabled()) { + log.trace("JacksonTypeHandler(" + type + ")"); + } + Assert.notNull(type, "Type argument cannot be null", new Object[0]); + } + + @Override + protected List parse(String json) { + try { + // 这里进行了json解析,同样在这里也可以进行字段查询后的处理,如对象内部的手机号字段的加密展示等 + return objectMapper.readValue(json, new TypeReference>() { + }); + } catch (IOException var3) { + throw new RuntimeException(var3); + } + } + + @Override + protected String toJson(List obj) { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException var3) { + throw new RuntimeException(var3); + } + } + + public static void setObjectMapper(ObjectMapper om) { + objectMapper = om; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListSimpleDocDTOHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListSimpleDocDTOHandler.java new file mode 100644 index 000000000..3cd20f5a0 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/handler/ListSimpleDocDTOHandler.java @@ -0,0 +1,64 @@ +package cn.axzo.workflow.core.conf.handler; + +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import com.baomidou.mybatisplus.core.toolkit.Assert; +import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; + +import java.io.IOException; +import java.util.List; + +/** + * 签署业务文件数据映射转换 + * + * @author wangli + * @since 2025-04-03 11:24 + */ +@Slf4j +@MappedTypes({List.class}) +@MappedJdbcTypes({JdbcType.VARCHAR}) +public class ListSimpleDocDTOHandler extends AbstractJsonTypeHandler> { + private static ObjectMapper objectMapper = new ObjectMapper(); + + static { + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + public ListSimpleDocDTOHandler(Class type) { + if (log.isTraceEnabled()) { + log.trace("JacksonTypeHandler(" + type + ")"); + } + Assert.notNull(type, "Type argument cannot be null", new Object[0]); + } + + @Override + protected List parse(String json) { + try { + // 这里进行了json解析,同样在这里也可以进行字段查询后的处理,如对象内部的手机号字段的加密展示等 + return objectMapper.readValue(json, new TypeReference>() { + }); + } catch (IOException var3) { + throw new RuntimeException(var3); + } + } + + @Override + protected String toJson(List obj) { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException var3) { + throw new RuntimeException(var3); + } + } + + public static void setObjectMapper(ObjectMapper om) { + objectMapper = om; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/EndEventJsonConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/EndEventJsonConverter.java index d0906e27d..993bb9877 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/EndEventJsonConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/EndEventJsonConverter.java @@ -18,6 +18,7 @@ public class EndEventJsonConverter extends AbstractBpmnJsonConverter { public EndEvent convertJsonToElement(BpmnJsonNode node, Process process, String formKey) { EndEvent endEvent = new EndEvent(); endEvent.setId(END_EVENT_ID); + endEvent.setName("结束"); return endEvent; } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ServiceTaskJsonConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ServiceTaskJsonConverter.java index a68b6f18d..7e7f5d9a2 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ServiceTaskJsonConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ServiceTaskJsonConverter.java @@ -5,13 +5,10 @@ import cn.axzo.workflow.common.model.request.bpmn.BpmnCarbonCopyConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonNode; import org.flowable.bpmn.model.ExtensionAttribute; import org.flowable.bpmn.model.ExtensionElement; -import org.flowable.bpmn.model.FlowableListener; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.ServiceTask; -import org.flowable.bpmn.model.UserTask; import org.springframework.util.CollectionUtils; -import java.util.ArrayList; import java.util.List; import java.util.Objects; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/StartEventJsonConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/StartEventJsonConverter.java index 683aec44b..80d9be308 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/StartEventJsonConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/StartEventJsonConverter.java @@ -19,6 +19,7 @@ public class StartEventJsonConverter extends AbstractBpmnJsonConverter { private static void setMultiInstance(BpmnJsonNode node, UserTask userTask) { if (Objects.isNull(node.getProperty()) || Objects.equals(NODE_STARTER.getType(), userTask.getId()) - ||Objects.equals(Boolean.FALSE, node.getProperty().getIsMultiTask())) { + || Objects.equals(Boolean.FALSE, node.getProperty().getIsMultiTask())) { return; } MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = @@ -189,6 +194,46 @@ public class UserTaskJsonConverter extends AbstractBpmnJsonConverter { // 业务节点有一种不设置审批人的选项, 除"审批方式"外都是空配置, 所以在此直接中断后续的扩展属性配置 return; } + + // 签署确认节点特有的配置 + BpmnSignApproverLimit signApproverLimit = property.getSignApproverLimit(); + if (Objects.equals(NODE_SIGN, node.getType()) && Objects.nonNull(signApproverLimit) + && Objects.nonNull(signApproverLimit.getOrgLimit()) && Objects.nonNull(signApproverLimit.getRoleLimit())) { + ExtensionElement signApproveElement = new ExtensionElement(); + signApproveElement.setName(CONFIG_SIGN_APPROVER_LIMIT); + + ExtensionElement signApproverOrgLimitElement = new ExtensionElement(); + signApproverOrgLimitElement.setName(CONFIG_SIGN_APPROVER_ORG_LIMIT); + ExtensionAttribute signApproverOrgLimitValueAttribute = new ExtensionAttribute(); + signApproverOrgLimitValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE); + signApproverOrgLimitValueAttribute.setValue(signApproverLimit.getOrgLimit().getType()); + ExtensionAttribute signApproverOrgLimitDescAttribute = new ExtensionAttribute(); + signApproverOrgLimitDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC); + signApproverOrgLimitDescAttribute.setValue(signApproverLimit.getOrgLimit().getDesc()); + signApproverOrgLimitElement.addAttribute(signApproverOrgLimitValueAttribute); + signApproverOrgLimitElement.addAttribute(signApproverOrgLimitDescAttribute); + signApproveElement.addChildElement(signApproverOrgLimitElement); + + ExtensionElement signApproverRoleLimitElement = new ExtensionElement(); + signApproverRoleLimitElement.setName(CONFIG_SIGN_APPROVER_ROLE_LIMIT); + ExtensionAttribute signApproverRoleLimitValueAttribute = new ExtensionAttribute(); + signApproverRoleLimitValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE); + signApproverRoleLimitValueAttribute.setValue(signApproverLimit.getRoleLimit().getType()); + ExtensionAttribute signApproverRoleLimitDescAttribute = new ExtensionAttribute(); + signApproverRoleLimitDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC); + signApproverRoleLimitDescAttribute.setValue(signApproverLimit.getRoleLimit().getDesc()); + signApproverRoleLimitElement.addAttribute(signApproverRoleLimitValueAttribute); + signApproverRoleLimitElement.addAttribute(signApproverRoleLimitDescAttribute); + signApproveElement.addChildElement(signApproverRoleLimitElement); + + userTask.addExtensionElement(signApproveElement); + + if (Objects.isNull(property.getApproverSpecify())) { + property.setApproverSpecify(ApproverSpecifyEnum.signerRelated); + } + } + + // TODO 签署确认节点的一些额外配置,需要在此处处理。 if (Objects.nonNull(property.getApproverScope())) { // 审批人所在范围 ExtensionElement approverScopeElement = new ExtensionElement(); @@ -260,6 +305,24 @@ public class UserTaskJsonConverter extends AbstractBpmnJsonConverter { approverEmptyHandleTypeElement.addAttribute(approverEmptyHandleTypeDescAttribute); userTask.addExtensionElement(approverEmptyHandleTypeElement); } + + if (Objects.equals(Boolean.TRUE, property.getSignature())) { + // 电子签名开关 + ExtensionElement signatureElement = new ExtensionElement(); + signatureElement.setName(CONFIG_ACTIVITY_SIGNATURE); + + ExtensionAttribute approverSignatureValueAttribute = new ExtensionAttribute(); + approverSignatureValueAttribute.setName(ELEMENT_ATTRIBUTE_VALUE); + approverSignatureValueAttribute.setValue(property.getSignature().toString()); + signatureElement.addAttribute(approverSignatureValueAttribute); + + ExtensionAttribute approverSignatureDescAttribute = new ExtensionAttribute(); + approverSignatureDescAttribute.setName(ELEMENT_ATTRIBUTE_DESC); + approverSignatureDescAttribute.setValue("电子签名"); + signatureElement.addAttribute(approverSignatureDescAttribute); + + userTask.addExtensionElement(signatureElement); + } } /** diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/deletage/approverscope/PreTaskUserProcessor.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/deletage/approverscope/PreTaskUserProcessor.java index abbec1663..2afcf7d78 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/deletage/approverscope/PreTaskUserProcessor.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/deletage/approverscope/PreTaskUserProcessor.java @@ -38,6 +38,7 @@ public class PreTaskUserProcessor implements ApproverScopeProcessor { // 上级节点 String preActivityId = preUserTaskOpt.get().getId(); // 获取上一级节点 + @SuppressWarnings("unchecked") List assigners = (List) execution .getVariableLocal(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + preActivityId); List entOrg = CollectionUtils.emptyIfNull(assigners).stream() diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java index 61d24aa2d..db15327e2 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/behavior/CustomReceiveTaskActivityBehavior.java @@ -77,7 +77,7 @@ public class CustomReceiveTaskActivityBehavior extends ReceiveTaskActivityBehavi processEngineConfiguration.getEngineCfgKey()); // 解决两个业务节点相邻时,无法正确的往下流转的问题 - TaskHelper.deleteTask(task, null, true, true, true); + TaskHelper.deleteTask(task, "complete business node", false, true, true); } else { log.warn("ReceiveTask is null, executionId: {}, activityId: {}", execution.getId(), execution.getCurrentActivityId()); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java index f8ba27868..85513cf84 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/AbstractCommand.java @@ -2,6 +2,7 @@ package cn.axzo.workflow.core.engine.cmd; import cn.axzo.workflow.common.exception.WorkflowEngineException; +import lombok.extern.slf4j.Slf4j; import org.flowable.common.engine.impl.interceptor.Command; import org.flowable.common.engine.impl.interceptor.CommandContext; @@ -13,6 +14,7 @@ import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.ENGINE_EXEC_EXCE * @author wangli * @since 2024/7/1 13:59 */ +@Slf4j public abstract class AbstractCommand implements Command { public abstract String paramToJsonString(); @@ -24,7 +26,8 @@ public abstract class AbstractCommand implements Command { } catch (WorkflowEngineException e) { throw e; } catch (Exception e) { - throw new WorkflowEngineException(ENGINE_EXEC_EXCEPTION); + log.warn(e.getMessage(), e); + throw new WorkflowEngineException(ENGINE_EXEC_EXCEPTION, e, e.getMessage()); } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java index e78406f28..45a2eef3c 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAbortProcessInstanceCmd.java @@ -103,9 +103,9 @@ public class CustomAbortProcessInstanceCmd extends AbstractCommand impleme Map variables = new HashMap<>(); variables.put(INTERNAL_DELETE_PROCESS_FLAG, INTERNAL_PROCESS_TYPE_ABORT); variables.put(INTERNAL_PROCESS_DELETE_REASON, reason); - variables.put(INTERNAL_END_TENANT_ID, assigner.getTenantId()); - variables.put(INTERNAL_END_USER_ID, assigner.buildAssigneeId()); - variables.put(INTERNAL_END_USER_NAME, assigner.getAssignerName()); +// variables.put(INTERNAL_END_TENANT_ID, assigner.getTenantId()); +// variables.put(INTERNAL_END_USER_ID, assigner.buildAssigneeId()); +// variables.put(INTERNAL_END_USER_NAME, assigner.getAssignerName()); runtimeService.setVariables(instance.getId(), variables); CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext, @@ -118,6 +118,8 @@ public class CustomAbortProcessInstanceCmd extends AbstractCommand impleme batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList, assigner); completeVirtualTask(commandContext, task); + + runtimeService.setVariable(processInstanceId, CLOSE_PROCESS_ASSIGNER, assigner); return null; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerCmd.java index cf0b1d48b..df374ad18 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomActivityTriggerCmd.java @@ -52,7 +52,7 @@ public class CustomActivityTriggerCmd extends AbstractCommand implements S if (Objects.isNull(task)) { throw new WorkflowEngineException(ACTIVITY_TRIGGER_NOT_EXISTS, dto.getTriggerId()); } - addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "已处理"); + addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, "已同意"); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); runtimeService.trigger(dto.getTriggerId()); return null; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAddTimerJobCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAddTimerJobCmd.java new file mode 100644 index 000000000..31cae5f7b --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomAddTimerJobCmd.java @@ -0,0 +1,77 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.workflow.common.model.dto.TermNodeAddTimerJobDTO; +import cn.axzo.workflow.core.converter.json.NotSupportConverter; +import cn.axzo.workflow.core.engine.job.AsyncTermNodeAlterJobHandler; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.TimerEventDefinition; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.ManagementService; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.jobexecutor.TimerEventHandler; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.impl.util.TimerUtil; +import org.flowable.engine.runtime.Execution; +import org.flowable.job.service.impl.persistence.entity.TimerJobEntity; +import org.springframework.util.CollectionUtils; + +import java.io.Serializable; +import java.util.List; + +/** + * 自定义添加定时任务的逻辑 + * + * @author wangli + * @since 2025-03-19 18:24 + */ +@Slf4j +public class CustomAddTimerJobCmd extends AbstractCommand implements Serializable { + private final TermNodeAddTimerJobDTO dto; + + public CustomAddTimerJobCmd(TermNodeAddTimerJobDTO dto) { + this.dto = dto; + } + + @Override + public String paramToJsonString() { + return JSON.toJSONString(dto); + } + + @Override + public Void executeInternal(CommandContext commandContext) { + log.info("CustomAddTimerJobCmd start. instanceId: {}, activityId: {}, timeCycle: {}", dto.getProcessInstanceId(), dto.getActivityId(), dto.getTimeCycle()); + ProcessEngineConfigurationImpl processEngineConfiguration = + CommandContextUtil.getProcessEngineConfiguration(commandContext); + + ManagementService managementService = processEngineConfiguration.getManagementService(); + String tableName = managementService.getTableName(Execution.class); + List list = processEngineConfiguration.getRuntimeService() + .createNativeExecutionQuery() + .sql("SELECT * FROM " + tableName + " WHERE PROC_INST_ID_ = #{instanceId} AND ACT_ID_ = #{activityId} AND IS_ACTIVE_ = 1 AND TASK_COUNT_ = 1") + .parameter("instanceId", dto.getProcessInstanceId()) + .parameter("activityId", dto.getActivityId()) + .list(); + if (CollectionUtils.isEmpty(list)) { + return null; + } + + if (list.get(list.size() - 1) instanceof ExecutionEntity) { + ExecutionEntity executionEntity = (ExecutionEntity) list.get(list.size() - 1); + TimerEventDefinition timerEventDefinition = new TimerEventDefinition(); + timerEventDefinition.setTimeCycle(dto.getTimeCycle()); + TimerJobEntity timerJob = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition, + new NotSupportConverter.NotSupportFlowElement(), + false, executionEntity, AsyncTermNodeAlterJobHandler.TYPE, + TimerEventHandler.createConfiguration(executionEntity.getCurrentActivityId(), null, + timerEventDefinition.getCalendarName())); + if (timerJob != null) { + CommandContextUtil.getTimerJobService().scheduleTimerJob(timerJob); + } + } else { + log.warn("未找到 execution entity"); + } + return null; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java index f04a60914..c727eb2a5 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskCmd.java @@ -1,10 +1,13 @@ package cn.axzo.workflow.core.engine.cmd; +import cn.axzo.workflow.common.enums.AttachmentTypeEnum; import cn.axzo.workflow.common.enums.BpmnFlowNodeType; +import cn.axzo.workflow.common.model.dto.SignatureDTO; import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import com.alibaba.fastjson.JSON; +import org.apache.commons.collections4.ListUtils; import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.engine.RuntimeService; @@ -17,17 +20,22 @@ import org.flowable.task.api.history.HistoricTaskInstanceQuery; import org.flowable.task.service.impl.persistence.entity.TaskEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import static cn.axzo.workflow.common.constant.BpmnConstants.CLOSE_PROCESS_ASSIGNER; import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE; import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_SPECIFY_NEXT_APPROVER; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGNATURE_COLLECTION; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment; @@ -101,11 +109,14 @@ public class CustomApproveTaskCmd extends AbstractCommand implements Seria if (Objects.nonNull(operationDesc)) { this.operationDesc = operationDesc; } else { - this.operationDesc = "(已通过)"; + this.operationDesc = "已同意"; } } @Override + /** + * TODO 重要说明,该类与 CustomApproveTaskWithFormCmd 作用等同,当逻辑有调整时,需同步调整 + */ public Void execute(CommandContext commandContext) { ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); @@ -140,10 +151,43 @@ public class CustomApproveTaskCmd extends AbstractCommand implements Seria nextApprover); } task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus()); + + // 记录电子签名的图片 + recordSignature(task, runtimeService); + executeSynchronous(task, taskService, runtimeService); + + runtimeService.setVariable(task.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, approver); return null; } + private void recordSignature(TaskEntity task, RuntimeService runtimeService) { + List signatures = runtimeService.getVariable(task.getProcessInstanceId(), SIGNATURE_COLLECTION, List.class); + if (Objects.isNull(signatures)) { + signatures = new ArrayList<>(); + } + Optional any = signatures.stream() + .filter(i -> Objects.equals(i.getActivityId(), task.getTaskDefinitionKey())).findAny(); + SignatureDTO dto = any.orElse(new SignatureDTO() + .setActivityId(task.getTaskDefinitionKey()) + .setActivityName(task.getName()) + .setSignatures(new ArrayList<>())); + ListUtils.emptyIfNull(attachmentList).stream() + .filter(i -> Objects.equals(i.getType(), AttachmentTypeEnum.signature)) + .findFirst() + .ifPresent(attachment -> dto.getSignatures().add(0, + new SignatureDTO.SignDetail() + .setSignature(attachment.getUrl()) + .setAdvice(advice))); + if (!any.isPresent()) { + signatures.add(dto); + } + + if (!CollectionUtils.isEmpty(signatures)) { + runtimeService.setVariable(task.getProcessInstanceId(), SIGNATURE_COLLECTION, signatures); + } + } + private void executeSynchronous(Task task, TaskService taskService, RuntimeService runtimeService) { if (StringUtils.hasText(task.getExecutionId())) { // 正常完成流程任务,审批通过 diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskWithFormCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskWithFormCmd.java index f2f355f6a..c33bc5473 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskWithFormCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomApproveTaskWithFormCmd.java @@ -1,8 +1,9 @@ package cn.axzo.workflow.core.engine.cmd; +import cn.axzo.workflow.common.enums.AttachmentTypeEnum; import cn.axzo.workflow.common.enums.BpmnFlowNodeType; import cn.axzo.workflow.common.exception.WorkflowEngineException; -import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import cn.axzo.workflow.common.model.dto.SignatureDTO; import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditWithFormDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; @@ -13,7 +14,7 @@ import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog; import cn.axzo.workflow.core.service.ExtAxBpmnFormRelationService; import cn.axzo.workflow.core.service.ExtAxProcessLogService; import com.alibaba.fastjson.JSON; -import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.collections4.ListUtils; import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.engine.RuntimeService; @@ -33,11 +34,12 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.io.Serializable; -import java.util.Collection; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; @@ -45,7 +47,9 @@ import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_FIELD_VALID import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE; import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_SPECIFY_NEXT_APPROVER; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGNATURE_COLLECTION; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; +import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_SUBMIT_FORM_VARIABLE; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment; @@ -59,6 +63,7 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask */ public class CustomApproveTaskWithFormCmd extends AbstractCommand implements Serializable { + private static final long serialVersionUID = 2366844598166684546L; private static final Logger log = LoggerFactory.getLogger(CustomApproveTaskWithFormCmd.class); private final String taskId; /** @@ -85,12 +90,12 @@ public class CustomApproveTaskWithFormCmd extends AbstractCommand implemen /** * 指定节点类型 */ - private List nodeTypes; + private final List nodeTypes; /** * 表单数据 */ - private Map formVariables; + private final Map formVariables; @Override public String paramToJsonString() { @@ -125,11 +130,14 @@ public class CustomApproveTaskWithFormCmd extends AbstractCommand implemen if (Objects.nonNull(operationDesc)) { this.operationDesc = operationDesc; } else { - this.operationDesc = "(已通过)"; + this.operationDesc = "已同意"; } } @Override + /** + * TODO 重要说明,该类与 CustomApproveTaskCmd 作用等同,当逻辑有调整时,需同步调整 + */ public Void execute(CommandContext commandContext) { ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); @@ -165,6 +173,10 @@ public class CustomApproveTaskWithFormCmd extends AbstractCommand implemen nextApprover); } task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + taskId, APPROVED.getStatus()); + + // 记录电子签名的图片 + recordSignature(task, runtimeService); + executeSynchronous(task, taskService, runtimeService, commandContext, historicTaskInstance.getTenantId()); return null; } @@ -183,12 +195,12 @@ public class CustomApproveTaskWithFormCmd extends AbstractCommand implemen if (CollectionUtils.isEmpty(logs) || logs.size() != 1) { throw new WorkflowEngineException(FORM_FIELD_VALIDATOR_ERROR); } - ExtAxProcessLog log = logs.get(0); - Map permissionMap = log.getFormFieldPermissionConf().stream() + ExtAxProcessLog axProcessLog = logs.get(0); + Map permissionMap = axProcessLog.getFormFieldPermissionConf().stream() .collect(Collectors.toMap(FormPermissionMetaInfo::getFieldId, Function.identity(), (s, t) -> s)); formVariables.forEach((k, v) -> { FormPermissionMetaInfo permission = permissionMap.getOrDefault(k, new FormPermissionMetaInfo()); - if (permission.getRequired()) { + if (Boolean.TRUE.equals(permission.getRequired())) { if (Objects.isNull(v)) { throw new WorkflowEngineException(FORM_FIELD_VALIDATOR_ERROR); } @@ -202,7 +214,7 @@ public class CustomApproveTaskWithFormCmd extends AbstractCommand implemen private void executeSynchronous(Task task, TaskService taskService, RuntimeService runtimeService, CommandContext commandContext, String tenantId) { ExtAxBpmnFormRelationService bpmnFormRelationService = SpringContextUtils.getBean(ExtAxBpmnFormRelationService.class); ExtAxBpmnFormRelation relation = bpmnFormRelationService.queryByBpmnDefinitionId(task.getProcessDefinitionId()); - if (Objects.nonNull(relation)) { + if (Objects.nonNull(relation) && !CollectionUtils.isEmpty(formVariables)) { log.info("带有表单的审批"); FormEngineConfigurationApi formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(commandContext); FormRepositoryService formRepositoryService = formEngineConfiguration.getFormRepositoryService(); @@ -211,19 +223,7 @@ public class CustomApproveTaskWithFormCmd extends AbstractCommand implemen .deploymentId(relation.getFormDeploymentId()) .singleResult(); Authentication.setAuthenticatedUserId(approver.buildAssigneeId()); - formVariables.entrySet().forEach(e -> { - if (e.getValue() instanceof Collection) { - List convertUploads = ((Collection) e.getValue()).stream().map(i -> { - if (i instanceof String) { - return UploadFieldDTO.toObject(String.valueOf(i)).toSpecString(); - } else { - ObjectMapper objectMapper = SpringContextUtils.getBean(ObjectMapper.class); - return objectMapper.convertValue(i, UploadFieldDTO.class).toSpecString(); - } - }).collect(Collectors.toList()); - e.setValue(StringUtils.collectionToCommaDelimitedString(convertUploads)); - } - }); + runtimeService.setVariableLocal(task.getProcessInstanceId(), TASK_SUBMIT_FORM_VARIABLE, formVariables); taskService.completeTaskWithForm(taskId, formDefinition.getId(), null, formVariables); Authentication.setAuthenticatedUserId(null); } else { @@ -239,4 +239,30 @@ public class CustomApproveTaskWithFormCmd extends AbstractCommand implemen } } + private void recordSignature(TaskEntity task, RuntimeService runtimeService) { + List signatures = runtimeService.getVariable(task.getProcessInstanceId(), SIGNATURE_COLLECTION, List.class); + if (Objects.isNull(signatures)) { + signatures = new ArrayList<>(); + } + Optional any = signatures.stream() + .filter(i -> Objects.equals(i.getActivityId(), task.getTaskDefinitionKey())).findAny(); + SignatureDTO dto = any.orElse(new SignatureDTO() + .setActivityId(task.getTaskDefinitionKey()) + .setActivityName(task.getName()) + .setSignatures(new ArrayList<>())); + ListUtils.emptyIfNull(attachmentList).stream() + .filter(i -> Objects.equals(i.getType(), AttachmentTypeEnum.signature)) + .findFirst() + .ifPresent(attachment -> dto.getSignatures().add(0, + new SignatureDTO.SignDetail() + .setSignature(attachment.getUrl()) + .setAdvice(advice))); + if (!any.isPresent()) { + signatures.add(dto); + } + + if (!CollectionUtils.isEmpty(signatures)) { + runtimeService.setVariable(task.getProcessInstanceId(), SIGNATURE_COLLECTION, signatures); + } + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskCmd.java index ef456b038..3a9b88a04 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBackTaskCmd.java @@ -1,9 +1,14 @@ package cn.axzo.workflow.core.engine.cmd; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO; import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.core.common.utils.BpmnModelUtils; import com.alibaba.fastjson.JSON; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.FlowNode; @@ -15,23 +20,27 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.impl.util.ProcessDefinitionUtil; import org.flowable.task.api.history.HistoricTaskInstance; -import org.flowable.task.api.history.HistoricTaskInstanceQuery; import org.flowable.task.service.TaskService; import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS; import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE; import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.BACKED; -import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE; -import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask; @@ -43,66 +52,122 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask public class CustomBackTaskCmd extends AbstractCommand implements Serializable { private static final long serialVersionUID = -1241290344311892346L; - private final BpmnTaskBackAuditDTO dto; + + private final List targetTaskIds; + private final String advice; + private final String operationDesc; + private final List attachmentList; + private final BpmnTaskDelegateAssigner operator; + private final String processInstanceId; + private final String currentActivityId; + private final String toActivityId; + private final boolean validateApprover; private static final String OPERATION_DESC = "回退至"; + public CustomBackTaskCmd(CustomBackParamsDto customBackParamsDto) { + if (customBackParamsDto == null) { + throw new NullPointerException("customBackParamsDto is null"); + } + if (customBackParamsDto.getOperator() == null) { + throw new NullPointerException("operator is null"); + } + this.targetTaskIds = customBackParamsDto.getTargetTaskIds(); + this.advice = customBackParamsDto.getAdvice(); + this.operationDesc = customBackParamsDto.getOperationDesc(); + this.attachmentList = customBackParamsDto.getAttachmentList(); + this.operator = customBackParamsDto.getOperator(); + this.processInstanceId = customBackParamsDto.getProcessInstanceId(); + this.currentActivityId = customBackParamsDto.getCurrentActivityId(); + this.toActivityId = customBackParamsDto.getToActivityId(); + this.validateApprover = customBackParamsDto.isValidateApprover(); + } + @Override public String paramToJsonString() { Map params = new HashMap<>(); - params.put("taskId", dto.getTaskId()); - params.put("advice", dto.getAdvice()); + params.put("targetTaskIds", JSON.toJSONString(this.targetTaskIds)); + params.put("advice", advice); params.put("operationDesc", OPERATION_DESC); - params.put("attachmentList", JSON.toJSONString(dto.getAttachmentList())); - params.put("approver", JSON.toJSONString(dto.getApprover())); - params.put("nodeTypes", JSON.toJSONString(dto.getNodeTypes())); + params.put("attachmentList", JSON.toJSONString(attachmentList)); + params.put("operator", JSON.toJSONString(operator)); + params.put("toActivityId", JSON.toJSONString(this.toActivityId)); + params.put("processInstanceId", JSON.toJSONString(this.processInstanceId)); + params.put("validateApprover", validateApprover); return JSON.toJSONString(params); } - public CustomBackTaskCmd(BpmnTaskBackAuditDTO dto) { - this.dto = dto; - } - @Override public Void execute(CommandContext commandContext) { ProcessEngineConfigurationImpl processEngineConfiguration = - CommandContextUtil.getProcessEngineConfiguration(commandContext); - HistoricTaskInstanceQuery taskQuery = - processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery(); - TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService(); - - HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult(); - - TaskEntity task = taskService.getTask(dto.getTaskId()); - validTask(historicTaskInstance, task, dto.getApprover(), dto.getNodeTypes()); - - Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId()); - FlowElement targetFlowElement = process.getFlowElement(dto.getToActivityId(), true); - if (Objects.isNull(targetFlowElement)) { - throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, dto.getToActivityId()); + CommandContextUtil.getProcessEngineConfiguration(commandContext); + List taskList = processEngineConfiguration.getHistoryService() + .createHistoricTaskInstanceQuery() + .taskDefinitionKey(currentActivityId) + .processInstanceId(processInstanceId) + .list(); + List valueableTaskList = taskList.stream() + .filter(hi -> { + if (hi.getEndTime() != null) { + return false; + } + if (!CollectionUtils.isEmpty(targetTaskIds)) { + return targetTaskIds.contains(hi.getId()); + } + return true; + }) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(valueableTaskList)) { + throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS); } - FlowElement sourceFlowElement = process.getFlowElement(task.getTaskDefinitionKey(), true); + TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService(); + if (validateApprover) { + valueableTaskList.forEach(hi -> validTask(hi, taskService.getTask(hi.getId()), operator, null)); + } + String processDefinitionId = valueableTaskList.get(0).getProcessDefinitionId(); + Process process = ProcessDefinitionUtil.getProcess(processDefinitionId); + FlowElement targetFlowElement = process.getFlowElement(toActivityId, true); + if (Objects.isNull(targetFlowElement)) { + throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, toActivityId); + } + FlowElement sourceFlowElement = process.getFlowElement(currentActivityId, true); // 退回节点到当前节点不可达到,不允许退回 if (!BpmnModelUtils.isReachable(process, (FlowNode) targetFlowElement, (FlowNode) sourceFlowElement)) { - throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId()); + throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, toActivityId); } - batchAddAttachment(commandContext, task.getProcessInstanceId(), task, dto.getAttachmentList(), dto.getApprover()); - - Authentication.setAuthenticatedUserId(dto.getApprover().buildAssigneeId()); - addComment(commandContext, task, COMMENT_TYPE_ADVICE, dto.getAdvice()); - addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, OPERATION_DESC + targetFlowElement.getName()); - Authentication.setAuthenticatedUserId(null); - RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); - task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + dto.getTaskId(), BACKED.getStatus()); - + valueableTaskList.forEach(th -> { + TaskEntity task = taskService.getTask(th.getId()); + Authentication.setAuthenticatedUserId(operator.buildAssigneeId()); + batchAddAttachment(commandContext, processInstanceId, task, attachmentList, operator); + addComment(commandContext, task, COMMENT_TYPE_ADVICE, advice); + addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, StringUtils.hasText(operationDesc) ? operationDesc : OPERATION_DESC + targetFlowElement.getName()); + Authentication.setAuthenticatedUserId(null); + task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), BACKED.getStatus()); + }); // 移除回退到的指定节点的变量,让 EngineExecutionStartListener 重新计算该节点的人 - runtimeService.removeVariable(task.getProcessInstanceId(), INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + dto.getToActivityId()); + runtimeService.removeVariable(processInstanceId, INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + toActivityId); runtimeService.createChangeActivityStateBuilder() - .processInstanceId(task.getProcessInstanceId()) - .moveActivityIdsToSingleActivityId(Collections.singletonList(task.getTaskDefinitionKey()), dto.getToActivityId()) - .changeState(); + .processInstanceId(processInstanceId) + .moveActivityIdsToSingleActivityId(Collections.singletonList(currentActivityId), toActivityId) + .changeState(); return null; } + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static final class CustomBackParamsDto { + private List targetTaskIds; + private String advice; + private String operationDesc; + private List attachmentList; + private BpmnTaskDelegateAssigner operator; + private String processInstanceId; + private String currentActivityId; + private String toActivityId; + private boolean validateApprover; + } + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java index 8cecd996c..243063932 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java @@ -1,7 +1,7 @@ package cn.axzo.workflow.core.engine.cmd; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper; import com.alibaba.fastjson.JSON; import org.flowable.common.engine.impl.interceptor.CommandContext; @@ -24,16 +24,16 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.ACTIVITY_BIZ_SET_ASSIGNEE_ERROR; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.ACTIVITY_CANT_SET_ASSIGNEE; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.PROCESS_CANT_SET_ASSIGNEE; import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT; import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; -import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS; -import static cn.axzo.workflow.common.code.BpmnTaskRespCode.ACTIVITY_BIZ_SET_ASSIGNEE_ERROR; -import static cn.axzo.workflow.common.code.BpmnTaskRespCode.ACTIVITY_CANT_SET_ASSIGNEE; -import static cn.axzo.workflow.common.code.BpmnTaskRespCode.PROCESS_CANT_SET_ASSIGNEE; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount; /** @@ -133,6 +133,7 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand CommandContextUtil.getProcessEngineConfiguration(commandContext); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); + @SuppressWarnings("unchecked") List originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java index 509b1b986..4052f92f6 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceAsyncCmd.java @@ -1,8 +1,8 @@ package cn.axzo.workflow.core.engine.cmd; -import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.core.engine.job.AsyncCancelProcessInstanceJobHandler; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; @@ -17,17 +17,17 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity; import java.io.Serializable; import java.util.Objects; -import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.ABORTED; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_CANCEL; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.ABORTED; public class CustomCancelProcessInstanceAsyncCmd extends AbstractCommand implements Serializable { - private final BpmnProcessInstanceCancelDTO dto; + private final SuperBpmnProcessInstanceCancelDTO dto; - public CustomCancelProcessInstanceAsyncCmd(BpmnProcessInstanceCancelDTO dto) { + public CustomCancelProcessInstanceAsyncCmd(SuperBpmnProcessInstanceCancelDTO dto) { this.dto = dto; } @@ -38,14 +38,13 @@ public class CustomCancelProcessInstanceAsyncCmd extends AbstractCommand i @Override public Void execute(CommandContext commandContext) { - String processInstanceId = dto.getProcessInstanceId(), tenantId = dto.getTenantId(); + String processInstanceId = dto.getProcessInstanceId(); BpmnTaskDelegateAssigner initiator = dto.getInitiator(); ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); HistoryService historyService = processEngineConfiguration.getHistoryService(); HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery() .processInstanceId(processInstanceId) - .processInstanceTenantId(tenantId) .singleResult(); if (Objects.isNull(instance)) { throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java index 41bb9c051..f09b7bbf2 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCancelProcessInstanceCmd.java @@ -1,7 +1,7 @@ package cn.axzo.workflow.core.engine.cmd; import cn.axzo.workflow.common.exception.WorkflowEngineException; -import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO; import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.core.engine.model.AddComment; @@ -12,9 +12,12 @@ import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.task.api.Task; import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.io.Serializable; @@ -27,10 +30,9 @@ import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_CANCEL; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS; +import static cn.axzo.workflow.common.constant.BpmnConstants.CANCEL_PROCESS_NODE_DEF_KEY_NAME; +import static cn.axzo.workflow.common.constant.BpmnConstants.CLOSE_PROCESS_ASSIGNER; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_TENANT_ID; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_NAME; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_DELETE_REASON; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_CANCEL; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CANCEL; @@ -47,6 +49,7 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVir * @since 2024/1/2 18:19 */ public class CustomCancelProcessInstanceCmd extends AbstractCommand implements Serializable { + private static final long serialVersionUID = 6556399267210245511L; private final String processInstanceId; private final String tenantId; private final String reason; @@ -54,8 +57,9 @@ public class CustomCancelProcessInstanceCmd extends AbstractCommand implem private final String nodeName; private final List attachmentList; private final ExtAxHiTaskInstService extAxHiTaskInstService; + private final Boolean superAdmin; - public CustomCancelProcessInstanceCmd(BpmnProcessInstanceCancelDTO dto, + public CustomCancelProcessInstanceCmd(SuperBpmnProcessInstanceCancelDTO dto, ExtAxHiTaskInstService extAxHiTaskInstService) { this.processInstanceId = dto.getProcessInstanceId(); this.tenantId = dto.getTenantId(); @@ -63,6 +67,7 @@ public class CustomCancelProcessInstanceCmd extends AbstractCommand implem this.initiator = dto.getInitiator(); this.nodeName = StringUtils.hasText(dto.getNodeName()) ? dto.getNodeName() : "发起人撤回"; this.attachmentList = dto.getAttachmentList(); + this.superAdmin = Objects.nonNull(dto.getSuperAdmin()) && dto.getSuperAdmin(); this.extAxHiTaskInstService = extAxHiTaskInstService; } @@ -82,10 +87,13 @@ public class CustomCancelProcessInstanceCmd extends AbstractCommand implem CommandContextUtil.getProcessEngineConfiguration(commandContext); HistoryService historyService = processEngineConfiguration.getHistoryService(); - HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery() - .processInstanceId(processInstanceId) - .processInstanceTenantId(tenantId) - .singleResult(); + HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(processInstanceId); + if (Objects.equals(Boolean.TRUE, superAdmin)) { + query.processInstanceTenantId(tenantId); + } + + HistoricProcessInstance instance = query.singleResult(); if (Objects.isNull(instance)) { throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS); } @@ -94,7 +102,7 @@ public class CustomCancelProcessInstanceCmd extends AbstractCommand implem throw new WorkflowEngineException(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } - if (Objects.isNull(initiator) || !initiator.comparePersonIdToOther(instance.getStartUserId())) { + if (!Objects.equals(Boolean.TRUE, superAdmin) && !initiator.comparePersonIdToOther(instance.getStartUserId())) { throw new WorkflowEngineException(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } @@ -102,26 +110,37 @@ public class CustomCancelProcessInstanceCmd extends AbstractCommand implem throw new WorkflowEngineException(PROCESS_INSTANCE_CANT_CANCEL); } - + List tasks = processEngineConfiguration.getTaskService() + .createTaskQuery() + .processInstanceId(processInstanceId) + .active() + .list(); + String taskDefKey = ""; + if (!CollectionUtils.isEmpty(tasks)) { + taskDefKey = tasks.get(0).getTaskDefinitionKey(); + } Map variables = new HashMap<>(); - variables.put(INTERNAL_END_TENANT_ID, tenantId); - variables.put(INTERNAL_END_USER_ID, initiator.buildAssigneeId()); - variables.put(INTERNAL_END_USER_NAME, initiator.getAssignerName()); +// variables.put(INTERNAL_END_TENANT_ID, tenantId); +// variables.put(INTERNAL_END_USER_ID, initiator.buildAssigneeId()); +// variables.put(INTERNAL_END_USER_NAME, initiator.getAssignerName()); variables.put(INTERNAL_DELETE_PROCESS_FLAG, INTERNAL_PROCESS_TYPE_CANCEL); variables.put(INTERNAL_PROCESS_DELETE_REASON, reason); + variables.put(CANCEL_PROCESS_NODE_DEF_KEY_NAME, taskDefKey); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); runtimeService.setVariables(instance.getId(), variables); CommandContextUtil.getAgenda(commandContext).planOperation(new DeleteProcessInstanceOperation(commandContext, - processInstanceId, extAxHiTaskInstService, CANCELLED)); + processInstanceId, extAxHiTaskInstService, CANCELLED)); // 添加自定义的节点,用于展示最后的操作 TaskEntity task = createVirtualTask(commandContext, extAxHiTaskInstService, processInstanceId, - nodeName, NODE_CANCEL.getType(), reason, initiator, CANCELLED.getStatus(), new AddComment(CANCELLED.getDesc())); + nodeName, NODE_CANCEL.getType(), reason, initiator, CANCELLED.getStatus(), new AddComment(CANCELLED.getDesc())); batchAddAttachment(commandContext, processInstanceId, task, attachmentList, initiator); completeVirtualTask(commandContext, task); + + runtimeService.setVariable(task.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, initiator); return null; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java index d309f18ad..b7748ace0 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java @@ -23,9 +23,7 @@ import org.springframework.util.CollectionUtils; import java.io.Serializable; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -37,7 +35,6 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COUNTE import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.completeVirtualTask; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask; -import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.deleteMultiTasks; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.getDuplicatePendingTasks; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount; @@ -173,7 +170,10 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand implemen message.append("、"); } } - message.append("等").append(targetTaskAssigneeList.size()).append("人进行审批"); + if (targetTaskAssigneeList.size() > end) { + message.append("等"); + } + message.append(targetTaskAssigneeList.size()).append("人进行审批"); Task virtualTask = createVirtualTask(commandContext, extAxHiTaskInstService, task.getProcessInstanceId(), task.getName(), task.getTaskDefinitionKey(), advice, originTaskAssignee, COUNTERSIGN.getStatus(), new AddComment(message.toString())); batchAddAttachment(commandContext, task.getProcessInstanceId(), task, attachmentList, originTaskAssignee); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetFormInstanceLatestValuesCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetFormInstanceLatestValuesCmd.java new file mode 100644 index 000000000..169dfb5e0 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetFormInstanceLatestValuesCmd.java @@ -0,0 +1,145 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.form.api.FormInstance; +import org.flowable.form.api.FormService; +import org.flowable.form.engine.FormEngineConfiguration; +import org.flowable.form.engine.impl.cmd.GetFormInstanceValuesCmd; +import org.flowable.form.engine.impl.util.CommandContextUtil; +import org.flowable.variable.api.history.HistoricVariableInstance; +import org.joda.time.LocalDate; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS; +import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_INSTANCE_DATA_NOT_FOUND; +import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_SUBMIT_FORM_VARIABLE; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; + +/** + * 自定义的获取表单实例数据的命令 + * + * @author wangli + * @since 2025-01-21 10:24 + */ +public class CustomGetFormInstanceLatestValuesCmd extends GetFormInstanceValuesCmd { + private String processInstanceId; + private Boolean throwException; + + public CustomGetFormInstanceLatestValuesCmd(String processInstanceId) { + this(null, processInstanceId, true); + } + + public CustomGetFormInstanceLatestValuesCmd(String processInstanceId, Boolean throwException) { + this(null, processInstanceId, throwException); + } + + public CustomGetFormInstanceLatestValuesCmd(String formInstanceId, String processInstanceId, + Boolean throwException) { + super(formInstanceId); + this.processInstanceId = processInstanceId; + this.throwException = throwException; + } + + @Override + public byte[] execute(CommandContext commandContext) { + if (StringUtils.hasText(formInstanceId) && !StringUtils.hasText(processInstanceId)) { + return super.execute(commandContext); + } + ProcessEngineConfigurationImpl processEngineConfiguration = + org.flowable.engine.impl.util.CommandContextUtil.getProcessEngineConfiguration(commandContext); + + HistoricProcessInstance instance = + processEngineConfiguration.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(instance)) { + throw new WorkflowEngineException(PROCESS_INSTANCE_ID_NOT_EXISTS, processInstanceId); + } + Map variableLocal = new HashMap<>(); + if (Objects.equals(PROCESSING, BpmnProcessInstanceResultEnum.valueOfStatus(instance.getBusinessStatus()))) { + Map taskSubmitFormVariables = + processEngineConfiguration.getRuntimeService().getVariableLocal(processInstanceId, + TASK_SUBMIT_FORM_VARIABLE, Map.class); + if (!CollectionUtils.isEmpty(taskSubmitFormVariables)) { + variableLocal.putAll(taskSubmitFormVariables); + } + } else { + HistoricVariableInstance historicVariableInstance = processEngineConfiguration.getHistoryService() + .createHistoricVariableInstanceQuery() + .processInstanceId(processInstanceId) + .variableName(TASK_SUBMIT_FORM_VARIABLE) + .singleResult(); + if (Objects.nonNull(historicVariableInstance)) { + variableLocal.putAll((Map) historicVariableInstance.getValue()); + } + } + FormEngineConfiguration formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(); + if (CollectionUtils.isEmpty(variableLocal)) { + + FormService formService = formEngineConfiguration.getFormService(); + List formInstances = + formService.createFormInstanceQuery().processInstanceId(processInstanceId) + .orderBySubmittedDate().desc().listPage(0, 1); + if (!CollectionUtils.isEmpty(formInstances)) { + // { + // "values" : { + // "form_mutil_text1" : "表单变量 2", + // "form_text1" : "表单变量 1", + // "form_image1" : "[{\"fileUrl\":\"http://gips2.baidu.com/it/u=195724436,3554684702&fm=3028&app=3028&f=JPEG&fmt=auto?w=1280&h=960\"}]" + // } + //} + return formInstances.get(0).getFormValueBytes(); + } + if (throwException) { + throw new WorkflowEngineException(FORM_INSTANCE_DATA_NOT_FOUND, processInstanceId); + } else { + return null; + } + } else { + ObjectMapper objectMapper = formEngineConfiguration.getObjectMapper(); + ObjectNode submittedFormValuesJson = objectMapper.createObjectNode(); + + ObjectNode valuesNode = submittedFormValuesJson.putObject("values"); + variableLocal.forEach((k, variableValue) -> { + if (variableValue == null) { + valuesNode.putNull(k); + } else if (variableValue instanceof Long) { + valuesNode.put(k, (Long) variableValue); + + } else if (variableValue instanceof Double) { + valuesNode.put(k, (Double) variableValue); + + } else if (variableValue instanceof Boolean) { + valuesNode.put(k, (Boolean) variableValue); + + } else if (variableValue instanceof LocalDate) { + valuesNode.put(k, ((LocalDate) variableValue).toString()); + + } else { + valuesNode.put(k, variableValue.toString()); + } + }); + try { + return objectMapper.writeValueAsBytes(submittedFormValuesJson); + } catch (JsonProcessingException e) { + if (throwException) { + throw new WorkflowEngineException(FORM_INSTANCE_DATA_NOT_FOUND, processInstanceId); + } else { + return null; + } + } + } + + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetFormInstanceModelCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetFormInstanceModelCmd.java index 07c1de73f..e8765638b 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetFormInstanceModelCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetFormInstanceModelCmd.java @@ -1,19 +1,58 @@ package cn.axzo.workflow.core.engine.cmd; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.dto.AmountFieldDTO; +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import cn.axzo.workflow.core.engine.cmd.helper.FormFieldClone; +import cn.axzo.workflow.form.engine.model.CustomSimpleFormModel; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.delegate.Expression; +import org.flowable.common.engine.impl.el.VariableContainerWrapper; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.form.api.FormInstance; +import org.flowable.form.api.FormInstanceInfo; import org.flowable.form.api.FormInstanceQuery; import org.flowable.form.engine.FormEngineConfiguration; import org.flowable.form.engine.impl.cmd.GetFormInstanceModelCmd; import org.flowable.form.engine.impl.util.CommandContextUtil; +import org.flowable.form.model.ExpressionFormField; +import org.flowable.form.model.FormContainer; +import org.flowable.form.model.FormField; +import org.flowable.form.model.FormFieldTypes; +import org.flowable.form.model.Option; +import org.flowable.form.model.OptionFormField; +import org.flowable.form.model.SimpleFormModel; +import org.joda.time.LocalDate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_DATA_PARSE_ERROR_BY_CUSTOM_COMPONENT; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_CHANGE_SIGNATURE_ORDER; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_CUSTOM_COMPONENT; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_IMAGE; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_INPUT; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_RECTIFY_ORDER; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_TASK_ORDER; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_TEXTAREA; +import static cn.axzo.workflow.form.engine.cmd.CustomGetVariablesFromFormSubmissionCmd.fmtConvert; +import static cn.axzo.workflow.form.engine.cmd.CustomGetVariablesFromFormSubmissionCmd.parseToLocalDateTime; + /** * 自定义的获取表单模型和最新表单内容的命令实现 * @@ -78,4 +117,303 @@ public class CustomGetFormInstanceModelCmd extends GetFormInstanceModelCmd { log.info("未查询到流程实例关联的表单实例数据"); return null; } + + @Override + public void fillVariablesWithFormValues(Map submittedFormFieldMap, List allFields) { + for (FormField field : allFields) { + + JsonNode fieldValueNode = submittedFormFieldMap.get(field.getId()); + + if (fieldValueNode == null || fieldValueNode.isNull()) { + continue; + } + + String fieldType = field.getType(); + String fieldValue = fieldValueNode.asText(); + + if (FormFieldTypes.DATE.equals(fieldType)) { + try { + if (org.apache.commons.lang3.StringUtils.isNotEmpty(fieldValue)) { + LocalDateTime dateValue = parseToLocalDateTime(fieldValue, fmtConvert(field.getParam("fmt").toString())); + if (Objects.nonNull(dateValue)) { + variables.put(field.getId(), dateValue.format(DateTimeFormatter.ofPattern(fmtConvert(field.getParam("fmt").toString())))); + } else { + variables.put(field.getId(), fieldValue); + } + } + } catch (Exception e) { + log.error("Error parsing form date value for process instance {} and task {} with value {}", processInstanceId, taskId, fieldValue, e); + } +// } else if (FormFieldTypes.UPLOAD.equals(fieldType) || FormConstants.FORM_FIELD_TYPE_IMAGE.equals(fieldType)) { +// FormEngineConfiguration formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(); +// ObjectMapper objectMapper = formEngineConfiguration.getObjectMapper(); +// try { +// List uploadFiles = objectMapper.readValue(fieldValue, new TypeReference>() { +// }); +// variables.put(field.getId(), uploadFiles); +// } catch (JsonProcessingException e) { +// throw new WorkflowEngineException(FORM_DATA_PARSE_ERROR_BY_UPLOAD); +// } + } else if (fieldValueNode.isBoolean()) { + variables.put(field.getId(), fieldValueNode.asBoolean()); + + } else if (fieldValueNode.isLong()) { + variables.put(field.getId(), fieldValueNode.asLong()); + + } else if (fieldValueNode.isDouble()) { + variables.put(field.getId(), fieldValueNode.asDouble()); + + } else { + variables.put(field.getId(), fieldValue); + } + } + } + + @Override + protected void fillFormInstanceValues(FormInstanceInfo formInstanceModel, FormInstance formInstance, Map formInstanceFieldMap, ObjectMapper objectMapper) { + try { + JsonNode submittedNode = objectMapper.readTree(formInstance.getFormValueBytes()); + if (submittedNode == null) { + return; + } + + if (submittedNode.get("values") != null) { + JsonNode valuesNode = submittedNode.get("values"); + Iterator fieldIdIterator = valuesNode.fieldNames(); + while (fieldIdIterator.hasNext()) { + String fieldId = fieldIdIterator.next(); + JsonNode valueNode = valuesNode.get(fieldId); + formInstanceFieldMap.put(fieldId, valueNode); + } + } + + if (submittedNode.get("flowable_form_outcome") != null) { + JsonNode outcomeNode = submittedNode.get("flowable_form_outcome"); + if (!outcomeNode.isNull() && org.apache.commons.lang3.StringUtils.isNotEmpty(outcomeNode.asText())) { + formInstanceModel.setSelectedOutcome(outcomeNode.asText()); + } + } + + } catch (Exception e) { + throw new FlowableException("Error parsing form instance " + formInstance.getId(), e); + } + } + + @Override + protected void fillFormFieldValues(FormInstance formInstance, FormInstanceInfo formInstanceModel, CommandContext commandContext) { + FormEngineConfiguration formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(); + CustomSimpleFormModel formModel = new CustomSimpleFormModel((SimpleFormModel) formInstanceModel.getFormModel()); + List allFields = formModel.listAllFields(); + if (allFields != null) { + + Map formInstanceFieldMap = new HashMap<>(); + if (formInstance != null) { + fillFormInstanceValues(formInstanceModel, formInstance, formInstanceFieldMap, formEngineConfiguration.getObjectMapper()); + fillVariablesWithFormValues(formInstanceFieldMap, allFields); + } + + for (FormField field : allFields) { + if (field instanceof OptionFormField) { + OptionFormField optionFormField = (OptionFormField) field; + if (optionFormField.getOptionsExpression() != null) { + // Drop down options to be populated from an expression + Expression optionsExpression = formEngineConfiguration.getExpressionManager().createExpression(optionFormField.getOptionsExpression()); + Object value = null; + try { + value = optionsExpression.getValue(new VariableContainerWrapper(variables)); + } catch (Exception e) { + throw new FlowableException("Error getting value for optionsExpression: " + optionFormField.getOptionsExpression(), e); + } + if (value instanceof List) { + @SuppressWarnings("unchecked") + List

* 自定义指定实例下的指定名称的变量数据 * * @author wangli diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetModelByDefinitionIdCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetModelByDefinitionIdCmd.java new file mode 100644 index 000000000..0234b7505 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetModelByDefinitionIdCmd.java @@ -0,0 +1,42 @@ +package cn.axzo.workflow.core.engine.cmd; + +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; + +/** + * 通过流程定义ID获取流程模型 + * + * @author wangli + * @since 2025-04-19 10:17 + */ +public class CustomGetModelByDefinitionIdCmd implements Command { + private final String processDefinitionId; + + public CustomGetModelByDefinitionIdCmd(String processDefinitionId) { + this.processDefinitionId = processDefinitionId; + } + + @Override + public Model execute(CommandContext commandContext) { + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil + .getProcessEngineConfiguration(commandContext).getProcessEngineConfiguration(); + RepositoryService repositoryService = processEngineConfiguration.getRepositoryService(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(processDefinitionId) + .singleResult(); + + // 获取部署ID + String deploymentId = processDefinition.getDeploymentId(); + + // 通过部署ID查询模型 + Model model = repositoryService.createModelQuery() + .deploymentId(deploymentId) + .singleResult(); + return model; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetModelDocsCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetModelDocsCmd.java new file mode 100644 index 000000000..222f77e2d --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetModelDocsCmd.java @@ -0,0 +1,182 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.basics.common.BeanMapper; +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc; +import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper; +import cn.axzo.workflow.core.service.ExtAxReModelService; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.variable.api.history.HistoricVariableInstance; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_QUERY_ERROR; +import static cn.axzo.workflow.common.code.BpmnProcessDefinitionRespCode.PROCESS_DEFINITION_ID_ILLEGAL; +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_PROCESS_ENABLE_DOC_IDS; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; +import static cn.axzo.workflow.core.service.impl.ExtAxModelDocServiceImpl.buildQueryWrapper; + + +/** + * 自定义的获取指定流程实例或指定流程定义版本对应的模型中关联的文档元数据 + * + * @author wangli + * @since 2025-04-02 10:25 + */ +public class CustomGetModelDocsCmd implements Command> { + + private String processInstanceId; + /** + * eg. we1:1:202504021034000000001 or we1 + */ + private String processDefinitionId; + private String tenantId; + private Boolean filterEnable = true; + /** + * 该属性,只对流程实例入参生效,过滤出发起流程时,勾选的文档 + */ + private Boolean filterSelect = false; + private final ExtAxModelDocMapper extAxModelDocMapper; + private final ExtAxReModelService extAxReModelService; + + public CustomGetModelDocsCmd(String processInstanceId, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) { + this(processInstanceId, false, extAxModelDocMapper, extAxReModelService); + } + + public CustomGetModelDocsCmd(String processInstanceId, Boolean filterSelect, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) { + this(processInstanceId, filterSelect, true, extAxModelDocMapper, extAxReModelService); + } + + public CustomGetModelDocsCmd(String processInstanceId, Boolean filterSelect, Boolean filterEnable, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) { + this.processInstanceId = processInstanceId; + this.filterSelect = filterSelect; + this.extAxModelDocMapper = extAxModelDocMapper; + this.filterEnable = filterEnable; + this.extAxReModelService = extAxReModelService; + } + + public CustomGetModelDocsCmd(String processDefinitionId, String tenantId, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) { + this.processDefinitionId = processDefinitionId; + this.tenantId = StringUtils.hasText(tenantId) ? tenantId : NO_TENANT_ID; + this.extAxModelDocMapper = extAxModelDocMapper; + this.extAxReModelService = extAxReModelService; + } + + public CustomGetModelDocsCmd(String processInstanceId, String processDefinitionId, String tenantId, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) { + this.processInstanceId = processInstanceId; + this.processDefinitionId = processDefinitionId; + this.tenantId = tenantId; + this.extAxModelDocMapper = extAxModelDocMapper; + this.extAxReModelService = extAxReModelService; + } + + public CustomGetModelDocsCmd(String processInstanceId, String processDefinitionId, String tenantId, Boolean filterSelect, Boolean filterEnable, ExtAxModelDocMapper extAxModelDocMapper, ExtAxReModelService extAxReModelService) { + this.processInstanceId = processInstanceId; + this.processDefinitionId = processDefinitionId; + this.tenantId = tenantId; + this.filterSelect = filterSelect; + this.filterEnable = filterEnable; + this.extAxModelDocMapper = extAxModelDocMapper; + this.extAxReModelService = extAxReModelService; + } + + @Override + public List execute(CommandContext commandContext) { + check(); + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); + RepositoryService repositoryService = processEngineConfiguration.getRepositoryService(); + ProcessDefinition processDefinition; + List enableDocIds = new ArrayList<>(); + if (StringUtils.hasText(processInstanceId)) { + HistoryService historyService = processEngineConfiguration.getHistoryService(); + HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (filterSelect) { + if (Objects.isNull(instance)) { + return Collections.emptyList(); + } + if (Objects.equals(PROCESSING.getStatus(), instance.getBusinessStatus())) { + RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); + List docIds = (List) runtimeService.getVariable(processInstanceId, SIGN_PROCESS_ENABLE_DOC_IDS, List.class); + if (!CollectionUtils.isEmpty(docIds)) { + enableDocIds.addAll(docIds); + } + } else { + HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).variableName(SIGN_PROCESS_ENABLE_DOC_IDS).singleResult(); + if (Objects.nonNull(variableInstance)) { + enableDocIds.addAll((List) variableInstance.getValue()); + } + } + } + processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(instance.getProcessDefinitionId()).singleResult(); + } else { + List definitions = repositoryService.createProcessDefinitionQuery() + .processDefinitionKey(parseProcessDefinitionKey()) + .list(); + if (CollectionUtils.isEmpty(definitions)) { + return Collections.emptyList(); + } + Optional first = definitions.stream().filter(i -> i.getTenantId().equals(tenantId)) + .max(Comparator.comparing(ProcessDefinition::getVersion)); + if (first.isPresent()) { + processDefinition = first.get(); + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + Model model = commandExecutor.execute(new CustomGetModelByDefinitionIdCmd(processDefinition.getId())); + BpmnModelExtVO modelStatus = extAxReModelService.getStatusByModelId(model.getId()); + if (Objects.equals(0, modelStatus.getStatus())) { + processDefinition = definitions.stream().filter(i -> i.getTenantId().equals(NO_TENANT_ID)).max(Comparator.comparing(ProcessDefinition::getVersion)).get(); + } + } else { + processDefinition = first.orElseGet(() -> definitions.stream().filter(i -> i.getTenantId().equals(NO_TENANT_ID)).max(Comparator.comparing(ProcessDefinition::getVersion)).get()); + } + } + + ExtAxModelDoc query = new ExtAxModelDoc(); + query.setModelKey(processDefinition.getKey()); + if (Objects.equals(Boolean.TRUE, filterEnable)) { + query.setStatus(filterEnable); + } + query.setTenantId(processDefinition.getTenantId()); + query.setTempFile(false); + List docs = extAxModelDocMapper.selectList(buildQueryWrapper(query)); + + if (filterSelect) { + docs = docs.stream().filter(i -> enableDocIds.contains(i.getId())).collect(Collectors.toList()); + } + return BeanMapper.copyList(docs, DocBaseVO.class, (s, t) -> t.setFileType(FileTypeEnum.valueOfType(s.getFileType()))); + } + + private String parseProcessDefinitionKey() { + if (StringUtils.hasText(processDefinitionId)) { + return processDefinitionId.split(":")[0]; + } + throw new WorkflowEngineException(PROCESS_DEFINITION_ID_ILLEGAL); + } + + private void check() { + if (!StringUtils.hasText(processInstanceId) && !StringUtils.hasText(processDefinitionId)) { + throw new WorkflowEngineException(MODEL_FILE_QUERY_ERROR); + } + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetProcessInstanceVariablesCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetProcessInstanceVariablesCmd.java new file mode 100644 index 000000000..bd84f4cf0 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetProcessInstanceVariablesCmd.java @@ -0,0 +1,100 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.workflow.common.model.dto.SignatureDTO; +import cn.axzo.workflow.core.common.utils.SpringContextUtils; +import cn.axzo.workflow.core.service.CategoryService; +import com.alibaba.fastjson.JSON; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.HistoryService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGNATURE_COLLECTION; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_DEFINITION_KEY; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_END_TIME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INSTANCE_ID; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_START_TIME; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; + +/** + * 自定义获取流程实例中用于打印的变量命令实现 + * + * @author wangli + * @since 2025-01-21 11:47 + */ +public class CustomGetProcessInstanceVariablesCmd extends AbstractCommand> implements Serializable { + + private final String processInstanceId; + private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + + public CustomGetProcessInstanceVariablesCmd(String processInstanceId) { + this.processInstanceId = processInstanceId; + } + + @Override + public String paramToJsonString() { + Map params = new HashMap<>(); + params.put("processInstanceId", processInstanceId); + return JSON.toJSONString(params); + } + + @Override + public Map executeInternal(CommandContext commandContext) { + Map variables = new HashMap<>(); + variables.put(PRINT_VAR_PROCESS_INSTANCE_ID, processInstanceId); + if (!StringUtils.hasText(processInstanceId)) { + return variables; + } + + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); + HistoryService historyService = processEngineConfiguration.getHistoryService(); + HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(processInstanceId).includeProcessVariables().singleResult(); + // 添加流程开始时间 + variables.put(PRINT_VAR_PROCESS_START_TIME, sdf.format(instance.getStartTime())); + variables.put(PRINT_VAR_PROCESS_END_TIME, Objects.nonNull(instance.getEndTime()) ? sdf.format(instance.getEndTime()) : null); + // 添加流程业务 ID + addProcessDefinitionKey(variables, instance); + + Map processVariables = instance.getProcessVariables(); + // 发起人流程引擎内部模型,需要外部调用该命令时,再解析 + variables.put(PRINT_VAR_PROCESS_INITIATOR, processVariables.get(INTERNAL_INITIATOR)); + + // 电子签名 + addSignature(variables, (List) processVariables.get(SIGNATURE_COLLECTION)); + + return variables; + } + + private void addSignature(Map variables, List signatures) { + if (CollectionUtils.isEmpty(signatures)) { + return; + } + signatures.forEach(sign -> { + if (!Objects.equals(NODE_STARTER.getType(), sign.getActivityId())) { + variables.put(sign.getActivityId(), sign.getSignatures()); + } + }); + } + + private void addProcessDefinitionKey(Map variables, HistoricProcessInstance historicProcessInstance) { + CategoryService categoryService = SpringContextUtils.getBean(CategoryService.class); + categoryService.get(BPM_MODEL_CATEGORY, historicProcessInstance.getProcessDefinitionKey()).ifPresent(category -> { + variables.put(PRINT_VAR_PROCESS_DEFINITION_KEY, category.getLabel() + "(" + category.getValue() + ")"); + }); + } + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetProcessInstanceVariablesToObjectCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetProcessInstanceVariablesToObjectCmd.java new file mode 100644 index 000000000..6c119416e --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomGetProcessInstanceVariablesToObjectCmd.java @@ -0,0 +1,311 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.workflow.common.enums.VarTypeEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.dto.ContactsPersonDTO; +import cn.axzo.workflow.common.model.dto.SignatureDTO; +import cn.axzo.workflow.common.model.dto.VariableObjectDTO; +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarSearchDto; +import cn.axzo.workflow.common.model.response.category.CategoryGroupVarItemVo; +import cn.axzo.workflow.common.model.response.category.CategoryItemVO; +import cn.axzo.workflow.core.common.utils.SpringContextUtils; +import cn.axzo.workflow.core.service.CategoryGroupService; +import cn.axzo.workflow.core.service.CategoryService; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.HistoryService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.form.api.FormInfo; +import org.flowable.form.api.FormRepositoryService; +import org.flowable.form.api.FormService; +import org.flowable.form.model.FormContainer; +import org.flowable.form.model.FormField; +import org.flowable.form.model.FormFieldTypes; +import org.flowable.form.model.SimpleFormModel; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.FormModelRespCode.FORM_MODEL_NOT_EXISTS; +import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGNATURE_COLLECTION; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_VARIABLE; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_DEFINITION_KEY; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_DEFINITION_KEY_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_END_TIME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_END_TIME_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INSTANCE_ID; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INSTANCE_ID_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_START_TIME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_START_TIME_DESC; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; + +/** + * 自定义获取流程实例中的变量命令实现 + *

+ * 返回的变量包括如下: + * 1、业务名称、审批编号、发起事件、发起人对象信息、节点签名和意见 + * 2、业务管理中的变量 + * 3、表单中变量 + * + * @author wangli + * @since 2025-01-21 11:47 + */ +@Slf4j +public class CustomGetProcessInstanceVariablesToObjectCmd extends AbstractCommand> implements Serializable { + + private final String processInstanceId; + private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + private static final List SUPPORTED_FORM_TYPES = Lists.newArrayList("input", "date", "textarea", "image", "contacts", "amount"); + + public CustomGetProcessInstanceVariablesToObjectCmd(String processInstanceId) { + this.processInstanceId = processInstanceId; + } + + @Override + public String paramToJsonString() { + Map params = new HashMap<>(); + params.put("processInstanceId", processInstanceId); + return JSON.toJSONString(params); + } + + @Override + public List executeInternal(CommandContext commandContext) { + List returnVariables = new ArrayList<>(); + if (!StringUtils.hasText(processInstanceId)) { + return returnVariables; + } + returnVariables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_INSTANCE_ID) + .desc(PRINT_VAR_PROCESS_INSTANCE_ID_DESC) + .value(processInstanceId) + .build()); + + + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); + HistoryService historyService = processEngineConfiguration.getHistoryService(); + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(processInstanceId).includeProcessVariables().singleResult(); + // 添加流程开始时间 + returnVariables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_START_TIME) + .desc(PRINT_VAR_PROCESS_START_TIME_DESC) + .value(sdf.format(historicProcessInstance.getStartTime())) + .build()); + returnVariables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_END_TIME) + .desc(PRINT_VAR_PROCESS_END_TIME_DESC) + .value(Objects.nonNull(historicProcessInstance.getEndTime()) ? sdf.format(historicProcessInstance.getEndTime()) : null) + .build()); + + Map processVariables = historicProcessInstance.getProcessVariables(); + // 添加流程业务 ID 和业务下分组变量信息 + addProcessDefinitionKeyAndVariables(returnVariables, (Map) processVariables.getOrDefault(SIGN_VARIABLE, new HashMap()), historicProcessInstance); + + // 发起人流程引擎内部模型,需要外部调用该命令时,再解析; 当前命令所在 module 不支持调用二方接口 + returnVariables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_INITIATOR) + .desc(PRINT_VAR_PROCESS_INITIATOR_DESC) + .value(processVariables.get(INTERNAL_INITIATOR)) + .type(VariableObjectDTO.Type.obj) + .build()); + + // 电子签名 + addSignature(returnVariables, (List) processVariables.get(SIGNATURE_COLLECTION)); + + // 表单中的变量 + addFormFieldValue(commandContext, historicProcessInstance, returnVariables, processVariables); + + return returnVariables; + } + + private void addFormFieldValue(CommandContext commandContext, HistoricProcessInstance instance, List variables, Map processVariables) { + Map formFieldValues = new HashMap<>(); + byte[] formInstanceValue = commandContext.getCommandExecutor().execute(new CustomGetFormInstanceLatestValuesCmd(processInstanceId, false)); + if (!ObjectUtils.isEmpty(formInstanceValue)) { + JSONObject treeNode = JSON.parseObject(new String(formInstanceValue)); + formFieldValues.putAll(treeNode.getJSONObject("values").getInnerMap()); + } + + FormRepositoryService formRepositoryService = CommandContextUtil.getFormRepositoryService(commandContext); + FormInfo formModel; + try { + formModel = formRepositoryService.getFormModelByKey(instance.getProcessDefinitionKey(), instance.getTenantId(), true); + } catch (FlowableObjectNotFoundException e) { + log.warn("can't found form model"); + throw new WorkflowEngineException(FORM_MODEL_NOT_EXISTS); + } + if (CollectionUtils.isEmpty(formFieldValues)) { + + FormService formService = CommandContextUtil.getFormService(commandContext); + formFieldValues.putAll(formService.getVariablesFromFormSubmission(formModel, processVariables, null)); + } + + if (CollectionUtils.isEmpty(formFieldValues)) { + return; + } + + + List formFields = ((SimpleFormModel) formModel.getFormModel()).getFields(); + + formFields.forEach(formField -> { + FormContainer formContainer = (FormContainer) formField; + formContainer.getFields().get(0).forEach(field -> { + if (!(field instanceof FormContainer) && SUPPORTED_FORM_TYPES.contains(field.getType())) { + Object fieldValue = formFieldValues.getOrDefault(field.getId(), null); + if (Objects.nonNull(fieldValue)) { + if (Objects.equals(field.getType(), FormFieldTypes.AMOUNT)) { + if (StringUtils.hasText(fieldValue.toString())) { + JSONObject jsonObject = JSON.parseObject(fieldValue.toString()); + BigDecimal standardNumerals = jsonObject.getBigDecimal("standardNumerals"); + if (Objects.nonNull(standardNumerals)) { + variables.add(VariableObjectDTO.builder() + .key(field.getId() + "_standardNumerals") + .desc(field.getName() + "小写") + .value(standardNumerals) + .type(convert(field.getType())) + .build()); + } + if (Boolean.parseBoolean(field.getParam("toUpper").toString())) { + String uppercaseNumerals = jsonObject.getString("uppercaseNumerals"); + if (StringUtils.hasText(uppercaseNumerals)) { + variables.add(VariableObjectDTO.builder() + .key(field.getId() + "_uppercaseNumerals") + .desc(field.getName() + "大写") + .value(uppercaseNumerals) + .type(convert(field.getType())) + .build()); + } + } + } + } else if (Objects.equals(field.getType(), "contacts")) { + if (StringUtils.hasText(fieldValue.toString()) + && fieldValue.toString().startsWith("[") + && fieldValue.toString().endsWith("]")) { + List names = JSON.parseArray((String) fieldValue, ContactsPersonDTO.class) + .stream().map(ContactsPersonDTO::getRealName).collect(Collectors.toList()); + variables.add(VariableObjectDTO.builder() + .key(field.getId()) + .desc(field.getName()) + .value(StringUtils.collectionToCommaDelimitedString(names)) + .type(convert(field.getType())) + .build()); + } + } else { + variables.add(VariableObjectDTO.builder() + .key(field.getId()) + .desc(field.getName()) + .value(fieldValue) + .type(convert(field.getType())) + .build()); + } + } + } + }); + }); + } + + private void addSignature(List variables, List signatures) { + if (CollectionUtils.isEmpty(signatures)) { + return; + } + signatures.forEach(sign -> { + if (!Objects.equals(NODE_STARTER.getType(), sign.getActivityId())) { + variables.add(VariableObjectDTO.builder() + .key(sign.getActivityId()) + .desc(sign.getActivityName()) + .value(sign.getSignatures()) + .type(VariableObjectDTO.Type.signatureAndAdvice) + .build()); + } + }); + } + + private void addProcessDefinitionKeyAndVariables(List variables, Map bizVariables, HistoricProcessInstance historicProcessInstance) { + CategoryService categoryService = SpringContextUtils.getBean(CategoryService.class); + categoryService.get(BPM_MODEL_CATEGORY, historicProcessInstance.getProcessDefinitionKey()).ifPresent(category -> { + variables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_DEFINITION_KEY) + .desc(PRINT_VAR_PROCESS_DEFINITION_KEY_DESC) + .value(category.getLabel() + "(" + category.getValue() + ")") + .build()); + // 添加业务下的变量 + addVariables(variables, category, bizVariables); + }); + } + + private void addVariables(List variables, CategoryItemVO category, Map bizVariables) { + CategoryGroupService categoryServiceGroup = SpringContextUtils.getBean(CategoryGroupService.class); + List groupVariables = categoryServiceGroup.searchGroupAndVarList(CategoryGroupVarSearchDto.builder() + .dictId(category.getId()) + .category(category.getValue()) + .build()); + + groupVariables.forEach(group -> { + if (!CollectionUtils.isEmpty(group.getVars())) { + group.getVars().forEach(variable -> { + Object value = bizVariables.getOrDefault(variable.getCode(), null); + if (Objects.isNull(value)) { + // 目前为了减少 wps 替换耗时,所以将没有值的变量,直接踢出 + return; + } + variables.add(VariableObjectDTO.builder() + .key(variable.getCode()) + .desc(group.getGroupName() + variable.getName()) + .value(value) + .type(convert(variable.getType())) + .build()); + }); + } + }); + + + } + + private VariableObjectDTO.Type convert(VarTypeEnum varType) { + switch (varType) { + case TEXT: + return VariableObjectDTO.Type.text; + case PICTURE: + return VariableObjectDTO.Type.img; + default: + return VariableObjectDTO.Type.obj; + } + } + + private VariableObjectDTO.Type convert(String type) { + switch (type) { + case "input": + case "text": + case "date": + case "textarea": + case "amount": + case "contacts": + return VariableObjectDTO.Type.text; + case "image": + return VariableObjectDTO.Type.img; + default: + return VariableObjectDTO.Type.obj; + } + } + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomNoticeDestinationUserSelectorCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomNoticeDestinationUserSelectorCmd.java index 3f88ca5e2..c6eba1159 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomNoticeDestinationUserSelectorCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomNoticeDestinationUserSelectorCmd.java @@ -9,7 +9,6 @@ import cn.axzo.workflow.core.engine.listener.EngineExecutionStartListener; import cn.axzo.workflow.core.engine.model.NoticeFlowElement; import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter; import com.alibaba.fastjson.JSON; -import org.flowable.common.engine.impl.interceptor.Command; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomOverrideFormVariablesByLatestInstanceCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomOverrideFormVariablesByLatestInstanceCmd.java new file mode 100644 index 000000000..7e19c0fb0 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomOverrideFormVariablesByLatestInstanceCmd.java @@ -0,0 +1,106 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.form.api.FormInstance; +import org.flowable.form.api.FormService; +import org.flowable.form.engine.FormEngineConfiguration; +import org.flowable.form.engine.impl.persistence.entity.FormInstanceEntity; +import org.flowable.form.engine.impl.util.CommandContextUtil; +import org.springframework.util.CollectionUtils; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_INSTANCE_DATA_NOT_FOUND; + +/** + * 覆写指定流程最后一次操作的表单中指定 key 的内容 + * + * @author wangli + * @since 2025-02-08 10:14 + */ +@Slf4j +public class CustomOverrideFormVariablesByLatestInstanceCmd extends AbstractCommand implements Serializable { + private static final long serialVersionUID = 1L; + private final String processInstanceId; + private final Map formVariables; + + public CustomOverrideFormVariablesByLatestInstanceCmd(String processInstanceId, Map formVariables) { + this.processInstanceId = processInstanceId; + this.formVariables = formVariables; + } + + @Override + public String paramToJsonString() { + Map params = new HashMap<>(); + params.put("processInstanceId", processInstanceId); + params.put("formVariables", formVariables); + return JSON.toJSONString(params); + } + + @Override + public Void executeInternal(CommandContext commandContext) { + FormEngineConfiguration formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(); + FormService formService = formEngineConfiguration.getFormService(); + List formInstances = formService.createFormInstanceQuery().processInstanceId(processInstanceId) + .orderBySubmittedDate().desc().listPage(0, 1); + if (CollectionUtils.isEmpty(formInstances)) { + throw new WorkflowEngineException(FORM_INSTANCE_DATA_NOT_FOUND, processInstanceId); + } + FormInstanceEntity formInstance = (FormInstanceEntity) formInstances.get(0); + + ObjectMapper objectMapper = CommandContextUtil.getFormEngineConfiguration().getObjectMapper(); + + try { + JsonNode jsonNode = objectMapper.readTree(formInstance.getFormValueBytes()); + if (Objects.isNull(jsonNode)) { + return null; + } + JsonNode valuesNode = jsonNode.get("values"); + if (Objects.nonNull(valuesNode)) { + ObjectNode valuesObjectNode = (ObjectNode) valuesNode; + + Iterator fieldIdIterator = valuesNode.fieldNames(); + while (fieldIdIterator.hasNext()) { + String fieldId = fieldIdIterator.next(); + + formVariables.forEach((k, v) -> { + if (Objects.equals(k, fieldId)) { + if (v instanceof Collection) { + try { + valuesObjectNode.set(k, new TextNode(objectMapper.writeValueAsString(v))); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } else { + valuesObjectNode.set(k, new TextNode(v.toString())); + } + } + }); + } + } + + byte[] formValueBytes = objectMapper.writeValueAsBytes(jsonNode); + log.info("更新后表单内容: {}", new String(formInstance.getFormValueBytes())); + formInstance.setFormValueBytes(formValueBytes); + CommandContextUtil.getFormInstanceEntityManager().update(formInstance); + } catch (Exception e) { + throw new FlowableException("Error parsing form instance " + formInstance.getId(), e); + } + return null; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java index f73f1ccff..c32b6a469 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRejectionTaskCmd.java @@ -24,12 +24,11 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import static cn.axzo.workflow.common.constant.BpmnConstants.CLOSE_PROCESS_ASSIGNER; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_TENANT_ID; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_NAME; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_DELETE_REASON; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_REJECT; +import static cn.axzo.workflow.common.constant.BpmnConstants.SKIP_MQ; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED; @@ -70,7 +69,7 @@ public class CustomRejectionTaskCmd extends AbstractCommand implements Ser if (Objects.nonNull(operationDesc)) { this.operationDesc = operationDesc; } else { - this.operationDesc = "(已驳回)"; + this.operationDesc = "已驳回"; } this.attachmentList = dto.getAttachmentList(); this.approver = dto.getApprover(); @@ -107,6 +106,7 @@ public class CustomRejectionTaskCmd extends AbstractCommand implements Ser task.getTaskDefinitionKey(), advice, Objects.equals(operationDesc, "自动驳回") ? null : approver, REJECTED.getStatus(), new AddComment(operationDesc)); + virtualTask.setTransientVariable(SKIP_MQ, true); batchAddAttachment(commandContext, task.getProcessInstanceId(), virtualTask, attachmentList, approver); completeVirtualTask(commandContext, virtualTask); @@ -119,12 +119,13 @@ public class CustomRejectionTaskCmd extends AbstractCommand implements Ser private void finishProcessInstance(CommandContext commandContext, RuntimeService runtimeService, Task task, String reason) { Map variables = new HashMap<>(); - variables.put(INTERNAL_END_TENANT_ID, approver.getTenantId()); - variables.put(INTERNAL_END_USER_NAME, approver.getAssignerName()); - variables.put(INTERNAL_END_USER_ID, approver.buildAssigneeId()); +// variables.put(INTERNAL_END_TENANT_ID, approver.getTenantId()); +// variables.put(INTERNAL_END_USER_NAME, approver.getAssignerName()); +// variables.put(INTERNAL_END_USER_ID, approver.buildAssigneeId()); variables.put(INTERNAL_DELETE_PROCESS_FLAG, INTERNAL_PROCESS_TYPE_REJECT); variables.put(INTERNAL_PROCESS_DELETE_REASON, reason); runtimeService.setVariables(task.getProcessInstanceId(), variables); + runtimeService.setVariable(task.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, approver); CommandContextUtil.getAgenda(commandContext) .planOperation(new DeleteProcessInstanceOperation(commandContext, task.getProcessInstanceId(), extAxHiTaskInstService, REJECTED)); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRemindTaskAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRemindTaskAsyncCmd.java new file mode 100644 index 000000000..64f17dac4 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRemindTaskAsyncCmd.java @@ -0,0 +1,106 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO; +import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler; +import cn.axzo.workflow.core.engine.job.AsyncRemindTaskJobHandler; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSON; +import org.apache.commons.collections.CollectionUtils; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.job.service.JobService; +import org.flowable.job.service.impl.persistence.entity.JobEntity; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_REMIND; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.REMIND_TASK_TOO_MANY; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_REMIND_ERROR_NOT_EXISTS; + +/** + * TODO + * + * @author wangli + * @since 2025-06-13 14:01 + */ +public class CustomRemindTaskAsyncCmd extends AbstractCommand implements Serializable { + private static final Logger logger = LoggerFactory.getLogger(CustomRemindTaskAsyncCmd.class); + private final BpmnTaskRemindDTO dto; + + public CustomRemindTaskAsyncCmd(BpmnTaskRemindDTO dto) { + this.dto = dto; + } + + @Override + public String paramToJsonString() { + return JSON.toJSONString(dto); + } + + @Override + public String executeInternal(CommandContext commandContext) { + ProcessEngineConfigurationImpl processEngineConfiguration = + CommandContextUtil.getProcessEngineConfiguration(commandContext); + RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); + ProcessInstance processInstance = + runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessInstanceId()).singleResult(); + if (Objects.isNull(processInstance)) { + throw new WorkflowEngineException(PROCESS_INSTANCE_CANT_REMIND, dto.getProcessInstanceId()); + } + + TaskService taskService = processEngineConfiguration.getTaskService(); + TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(dto.getProcessInstanceId()); + if (StringUtils.hasLength(dto.getTaskDefinitionKey())) { + taskQuery.taskDefinitionKey(dto.getTaskDefinitionKey()); + } + List list = taskQuery.active().list(); + if (CollectionUtils.isEmpty(list)) { + throw new WorkflowEngineException(TASK_REMIND_ERROR_NOT_EXISTS); + } + if (!StringUtils.hasText(dto.getTaskDefinitionKey())) { + List taskDefinitionKeys = list.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList()); + if (taskDefinitionKeys.isEmpty()) { + throw new WorkflowEngineException(TASK_REMIND_ERROR_NOT_EXISTS); + } else if (taskDefinitionKeys.size() > 1) { + throw new WorkflowEngineException(REMIND_TASK_TOO_MANY); + } else { + dto.setTaskDefinitionKey(taskDefinitionKeys.get(0)); + } + } + return startAsync(processEngineConfiguration, list.get(0)); + } + + private String startAsync(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) { + JobService jobService = processEngineConfiguration.getJobServiceConfiguration().getJobService(); + + JobEntity job = jobService.createJob(); + // 这里的 executionId 可为 null + job.setExecutionId(task.getExecutionId()); + job.setProcessInstanceId(task.getProcessInstanceId()); + job.setProcessDefinitionId(task.getProcessDefinitionId()); + job.setElementId(task.getTaskDefinitionKey()); + job.setElementName(task.getName()); + job.setJobHandlerType(AsyncRemindTaskJobHandler.TYPE); + job.setTenantId(task.getTenantId()); + + // 携带自定义的数据 + job.setCustomValues(JSONUtil.toJsonStr(dto)); + + // 创建异步任务并调度 + jobService.createAsyncJob(job, false); + jobService.scheduleAsyncJob(job); + return job.getId(); + } +} \ No newline at end of file diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRemindTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRemindTaskCmd.java new file mode 100644 index 000000000..8ba3f71ae --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomRemindTaskCmd.java @@ -0,0 +1,141 @@ +package cn.axzo.workflow.core.engine.cmd; + +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; +import cn.axzo.workflow.core.conf.SupportRefreshProperties; +import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder; +import cn.axzo.workflow.core.engine.event.MessagePushEventImpl; +import cn.axzo.workflow.core.engine.event.MessagePushEventType; +import com.alibaba.fastjson.JSON; +import com.google.common.collect.Lists; +import org.apache.commons.collections.CollectionUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.impl.util.ProcessDefinitionUtil; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_CANT_REMIND; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.REMIND_TASK_TOO_MANY; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_REMIND_ERROR_NOT_EXISTS; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature; + +/** + * 自定义(同步)催办任务的命令器实现 + * + * @author wangli + * @since 2025-06-13 13:39 + */ +public class CustomRemindTaskCmd extends AbstractCommand implements Serializable { + private static final Logger log = LoggerFactory.getLogger(CustomRemindTaskCmd.class); + private final SupportRefreshProperties refreshProperties; + /** + * 操作催办时的终端类型,管理端cm、工人端cmp + */ + private final String terminalType; + private final String processInstanceId; + private String taskDefinitionKey; + /** + * 催办方式 + *

+ * IM, 如果为空,默认是 IM + */ + private final List remindTypes; + + public CustomRemindTaskCmd(String terminalType, String processInstanceId, String taskDefinitionKey, List remindTypes, SupportRefreshProperties refreshProperties) { + this.terminalType = terminalType; + this.processInstanceId = processInstanceId; + this.taskDefinitionKey = taskDefinitionKey; + this.remindTypes = CollectionUtils.isEmpty(remindTypes) ? Lists.newArrayList("IM") : remindTypes; + this.refreshProperties = refreshProperties; + } + + @Override + public String paramToJsonString() { + Map params = new HashMap<>(); + params.put("terminalType", terminalType); + params.put("processInstanceId", processInstanceId); + params.put("taskDefinitionKey", taskDefinitionKey); + params.put("remindTypes", remindTypes); + return JSON.toJSONString(params); + } + + @Override + public Void executeInternal(CommandContext commandContext) { + ProcessEngineConfigurationImpl processEngineConfiguration = + CommandContextUtil.getProcessEngineConfiguration(commandContext); + RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); + ProcessInstance processInstance = + runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(processInstance)) { + throw new WorkflowEngineException(PROCESS_INSTANCE_CANT_REMIND, processInstanceId); + } + + TaskService taskService = processEngineConfiguration.getTaskService(); + TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId); + if (StringUtils.hasLength(taskDefinitionKey)) { + taskQuery.taskDefinitionKey(taskDefinitionKey); + } + List list = taskQuery.active().list(); + if (CollectionUtils.isEmpty(list)) { + throw new WorkflowEngineException(TASK_REMIND_ERROR_NOT_EXISTS); + } + if (!StringUtils.hasText(taskDefinitionKey)) { + List taskDefinitionKeys = list.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList()); + if (taskDefinitionKeys.isEmpty()) { + throw new WorkflowEngineException(TASK_REMIND_ERROR_NOT_EXISTS); + } else if (taskDefinitionKeys.size() > 1) { + throw new WorkflowEngineException(REMIND_TASK_TOO_MANY); + } else { + taskDefinitionKey = taskDefinitionKeys.get(0); + } + } + FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher(); + Process process = ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId()); + Optional noticeConfig = + BpmnMetaParserHelper.getNoticeConfig(process); + + Map variables = runtimeService.getVariables(processInstanceId, list.stream() + .map(i -> INTERNAL_TASK_RELATION_ASSIGNEE_INFO + i.getId()).collect(Collectors.toList())); + List assigners = variables.values().stream() + .map(BpmnTaskDelegateAssigner::toObjectCompatible) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + + // 过滤出未审批的任何,用选择的方式去发送消息 + remindTypes.forEach(type -> { + list.forEach(task -> { + assigners.stream().filter(i -> Objects.equals(task.getAssignee(), i.buildAssigneeId())).findAny().ifPresent(assigner -> { + MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.valueOf(type), + Lists.newArrayList(assigner), noticeConfig.orElse(null), processInstance.getProcessInstanceId(), + processInstance.getProcessDefinitionKey(), processInstance.getTenantId(), task.getId(), + getActivitySignature(process.getFlowElement(taskDefinitionKey)), + refreshProperties.getImTemplateCode(), terminalType); + eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey()); + }); + }); + }); + return null; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTermNodePausingAlertCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTermNodePausingAlertCmd.java index 3b6a2262a..35eeacb5b 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTermNodePausingAlertCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTermNodePausingAlertCmd.java @@ -1,9 +1,6 @@ package cn.axzo.workflow.core.engine.cmd; -import org.flowable.bpmn.model.TimerEventDefinition; import org.flowable.common.engine.impl.interceptor.CommandContext; -import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; -import org.flowable.engine.impl.util.CommandContextUtil; import java.io.Serializable; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java index f5e194021..480c051d6 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomTransferUserTaskCmd.java @@ -1,8 +1,9 @@ package cn.axzo.workflow.core.engine.cmd; +import cn.axzo.workflow.common.enums.ProcessTaskEventEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; -import cn.axzo.workflow.common.exception.WorkflowEngineException; import com.alibaba.fastjson.JSON; import com.google.common.collect.Lists; import org.apache.commons.lang3.StringUtils; @@ -32,6 +33,8 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERAT import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; +import static cn.axzo.workflow.common.constant.BpmnConstants.TRANSFER_TO; +import static cn.axzo.workflow.common.constant.BpmnConstants.TRANSFER_TO_ADVICE; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.TRANSFER; import static cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner.buildDummyAssigner; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment; @@ -114,6 +117,9 @@ public class CustomTransferUserTaskCmd extends AbstractCommand implements // 结束被转交任务 deleteMultiTasks(commandContext, Collections.singletonList(task)); + task.setTransientVariable(TRANSFER_TO, targetTaskAssignee); + task.setTransientVariable(TRANSFER_TO_ADVICE, advice); + processEngineConfiguration.getListenerNotificationHelper().executeTaskListeners(task, ProcessTaskEventEnum.PROCESS_TASK_TRANSFER.getTag()); return null; } @@ -132,6 +138,7 @@ public class CustomTransferUserTaskCmd extends AbstractCommand implements public void processAssignee(ProcessEngineConfigurationImpl processEngineConfiguration, Task task) { RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); + @SuppressWarnings("unchecked") List originAssingeeList = runtimeService.getVariable(task.getProcessInstanceId(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + task.getTaskDefinitionKey(), List.class); List resultList = new ArrayList<>(); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/helper/FormFieldClone.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/helper/FormFieldClone.java new file mode 100644 index 000000000..4c4e98fb8 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/helper/FormFieldClone.java @@ -0,0 +1,42 @@ +package cn.axzo.workflow.core.engine.cmd.helper; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.form.model.FormField; +import org.flowable.form.model.LayoutDefinition; +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.Objects; + +/** + * FormField Clone + * + * @author wangli + * @since 2025-01-24 00:11 + */ +@Slf4j +public class FormFieldClone { + + public static FormField clone(FormField formField) { + FormField clone = new FormField(); + clone.setId(formField.getId()); + clone.setName(formField.getName()); + clone.setType(formField.getType()); + clone.setValue(formField.getValue()); + clone.setRequired(formField.isRequired()); + clone.setReadOnly(formField.isReadOnly()); + clone.setOverrideId(formField.isOverrideId()); + clone.setPlaceholder(formField.getPlaceholder()); + + LayoutDefinition cloneLayout = new LayoutDefinition(); + if (Objects.nonNull(formField.getLayout())) { + cloneLayout.setRow(formField.getLayout().getRow()); + clone.setLayout(cloneLayout); + } + + if (!CollectionUtils.isEmpty(formField.getParams())) { + clone.setParams(new HashMap<>(formField.getParams())); + } + return clone; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/DocChangeEvent.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/DocChangeEvent.java new file mode 100644 index 000000000..af78c6866 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/DocChangeEvent.java @@ -0,0 +1,43 @@ +package cn.axzo.workflow.core.engine.event; + +import cn.axzo.workflow.common.model.dto.BizDocDTO; + +import java.util.List; + +/** + * 模型关联的文档变更事件对象 + * + * @author wangli + * @since 2025-04-07 16:51 + */ +public interface DocChangeEvent { + + /** + * 业务 ID + * + * @return + */ + String getKey(); + + /** + * 工作台 ID + * + * @return + */ + Long getWorkspaceId(); + + /** + * 修改后的文档 + * + * @return + */ + List getNewSettings(); + + /** + * 修改前的文档 + * + * @return + */ + List getOldSettings(); + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/DocChangeEventImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/DocChangeEventImpl.java new file mode 100644 index 000000000..bbec824ef --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/DocChangeEventImpl.java @@ -0,0 +1,68 @@ +package cn.axzo.workflow.core.engine.event; + +import cn.axzo.workflow.common.model.dto.BizDocDTO; +import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc; +import org.springframework.context.ApplicationEvent; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 模型关联的文档变更事件对象 + * + * @author wangli + * @since 2025-04-07 16:51 + */ +public class DocChangeEventImpl extends ApplicationEvent implements DocChangeEvent { + + private final String key; + + private final Long workspaceId; + + private final List newSettings; + + private final List oldSettings; + + public DocChangeEventImpl(String key, String workspaceId, List newSettings, List oldSettings) { + super(key); + this.key = key; + this.workspaceId = StringUtils.hasText(workspaceId) ? Long.valueOf(workspaceId) : null; + this.newSettings = convert(newSettings); + this.oldSettings = convert(oldSettings); + } + + private List convert(List docs) { + if (CollectionUtils.isEmpty(docs)) { + return Collections.emptyList(); + } + return docs.stream().map(i -> BizDocDTO.builder() + .archiveName(i.getTemplateName()) + .archiveCode(i.getTag()) + .enabled(i.getStatus()) + .build()).collect(Collectors.toList()); + } + + @Override + public String getKey() { + return key; + } + + @Override + public Long getWorkspaceId() { + return workspaceId; + } + + @Override + public List getNewSettings() { + return newSettings; + } + + @Override + public List getOldSettings() { + return oldSettings; + } + +} \ No newline at end of file diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEvent.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEvent.java index 6f57f3383..2faaf1362 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEvent.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEvent.java @@ -21,6 +21,8 @@ public interface MessagePushEvent extends FlowableEvent { BpmnApproveConf getProcessApproveConfig(); + Boolean getActivitySignature(); + String getProcessInstanceId(); String getProcessDefinitionId(); @@ -33,4 +35,8 @@ public interface MessagePushEvent extends FlowableEvent { String getTaskId(); + String getImTemplateCode(); + + String getTerminalType(); + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventBuilder.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventBuilder.java index 1ded63803..168032593 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventBuilder.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventBuilder.java @@ -1,9 +1,9 @@ package cn.axzo.workflow.core.engine.event; +import cn.axzo.workflow.common.exception.WorkflowEngineException; import cn.axzo.workflow.common.model.request.BpmnApproveConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; -import cn.axzo.workflow.common.exception.WorkflowEngineException; import java.util.List; @@ -11,6 +11,7 @@ import static cn.axzo.workflow.common.code.OtherRespCode.MESSAGE_PUSH_EVENT_BUIL import static cn.axzo.workflow.common.code.OtherRespCode.MES_PUSH_OBJECT_BUILD_ERROR; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.CARBON_COPY; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.CARBON_COPY_COMPLETE; +import static cn.axzo.workflow.core.engine.event.MessagePushEventType.IM; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.NOTICE; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.PENDING_COMPLETE; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.PENDING_PUSH; @@ -27,20 +28,27 @@ public class MessagePushEventBuilder { public static MessagePushEventImpl createEvent(MessagePushEventType type, List assigners, BpmnNoticeConf noticeConf, String processInstanceId, String processDefinitionKey, - String tenantId, String taskId) { + String tenantId, String taskId, Boolean activitySignature) { + return createEvent(type, assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId, activitySignature, null, null); + } + public static MessagePushEventImpl createEvent(MessagePushEventType type, List assigners, + BpmnNoticeConf noticeConf, String processInstanceId, String processDefinitionKey, + String tenantId, String taskId, Boolean activitySignature, String imTemplateCode, String terminalType) { switch (type) { case NOTICE: - return createNoticeEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId); + return createNoticeEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId, activitySignature); case PENDING_PUSH: throw new WorkflowEngineException(MESSAGE_PUSH_EVENT_BUILD_ERROR); case PENDING_COMPLETE: - return createPendingCompleteEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId); + return createPendingCompleteEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId, activitySignature); case CARBON_COPY: - return createCarbonCopyEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId); + return createCarbonCopyEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId, activitySignature); case CARBON_COPY_COMPLETE: - return createCarbonCopyCompleteEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId); + return createCarbonCopyCompleteEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, activitySignature); case SMS: - return createSmsEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId); + return createSmsEvent(assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId, activitySignature); + case IM: + return createImEvent(assigners, imTemplateCode, processInstanceId, terminalType); default: throw new WorkflowEngineException(MES_PUSH_OBJECT_BUILD_ERROR); } @@ -48,9 +56,10 @@ public class MessagePushEventBuilder { public static MessagePushEventImpl createNoticeEvent(List assigners, BpmnNoticeConf noticeConf - , String processInstanceId, String processDefinitionKey, String tenantId, String taskId) { + , String processInstanceId, String processDefinitionKey, String tenantId, String taskId, Boolean activitySignature) { MessagePushEventImpl newEvent = new MessagePushEventImpl(NOTICE, assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId); + newEvent.setActivitySignature(activitySignature); return newEvent; } @@ -61,18 +70,21 @@ public class MessagePushEventBuilder { String processDefinitionId, String processDefinitionKey, String currentTaskDefinitionKey, - String tenantId, String taskId) { + String tenantId, String taskId, + Boolean activitySignature) { MessagePushEventImpl newEvent = new MessagePushEventImpl(PENDING_PUSH, assigners, noticeConf, processApproveConf, processInstanceId, processDefinitionId, processDefinitionKey, currentTaskDefinitionKey, tenantId, taskId); + newEvent.setActivitySignature(activitySignature); return newEvent; } public static MessagePushEventImpl createPendingCompleteEvent(List assigners, BpmnNoticeConf noticeConf, String processInstanceId, String processDefinitionKey, - String tenantId, String taskId) { + String tenantId, String taskId, Boolean activitySignature) { MessagePushEventImpl newEvent = new MessagePushEventImpl(PENDING_COMPLETE, assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId); + newEvent.setActivitySignature(activitySignature); return newEvent; } @@ -84,18 +96,22 @@ public class MessagePushEventBuilder { public static MessagePushEventImpl createCarbonCopyEvent(List assigners, BpmnNoticeConf noticeConf, String processInstanceId, String processDefinitionKey, - String tenantId) { + String tenantId, + String taskId, + Boolean activitySignature) { MessagePushEventImpl newEvent = new MessagePushEventImpl(CARBON_COPY, assigners, noticeConf, - processInstanceId, processDefinitionKey, tenantId, null); + processInstanceId, processDefinitionKey, tenantId, taskId); + newEvent.setActivitySignature(activitySignature); return newEvent; } public static MessagePushEventImpl createCarbonCopyCompleteEvent(List assigners, BpmnNoticeConf noticeConf, String processInstanceId, String processDefinitionKey, - String tenantId) { + String tenantId, Boolean activitySignature) { MessagePushEventImpl newEvent = new MessagePushEventImpl(CARBON_COPY_COMPLETE, assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, null); + newEvent.setActivitySignature(activitySignature); return newEvent; } @@ -103,9 +119,17 @@ public class MessagePushEventBuilder { BpmnNoticeConf noticeConf, String processInstanceId, String processDefinitionKey, - String tenantId, String taskId) { + String tenantId, String taskId, Boolean activitySignature) { MessagePushEventImpl newEvent = new MessagePushEventImpl(SMS, assigners, noticeConf, processInstanceId, processDefinitionKey, tenantId, taskId); + newEvent.setActivitySignature(activitySignature); return newEvent; } + + public static MessagePushEventImpl createImEvent(List assigners, + String imTemplateCode, + String processInstanceId, + String terminalType) { + return new MessagePushEventImpl(IM, assigners, imTemplateCode, processInstanceId, terminalType); + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventImpl.java index d6eabf517..6870fd908 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventImpl.java @@ -23,12 +23,15 @@ public class MessagePushEventImpl implements MessagePushEvent { private List assigners; private BpmnNoticeConf noticeConfig; private BpmnApproveConf processApproveConfig; + private Boolean activitySignature; private String processInstanceId; private String processDefinitionId; private String processDefinitionKey; private String currentTaskDefinitionKey; private String tenantId; private String taskId; + private String imTemplateCode; + private String terminalType; public MessagePushEventImpl(FlowableEventType type) { if (type == null) { @@ -60,6 +63,13 @@ public class MessagePushEventImpl implements MessagePushEvent { this.taskId = taskId; } + public MessagePushEventImpl(FlowableEventType type, List assigners, String imTemplateCode, + String processInstanceId, String terminalType) { + this(type, assigners, null, processInstanceId, null, null, null); + this.imTemplateCode = imTemplateCode; + this.terminalType = terminalType; + } + /** * 使用该对象时需遵守,如果是 task 级的,一定要传 taskId, 其他参数则都是必传 *

@@ -180,6 +190,33 @@ public class MessagePushEventImpl implements MessagePushEvent { this.taskId = taskId; } + public void setActivitySignature(Boolean activitySignature) { + this.activitySignature = activitySignature; + } + + @Override + public Boolean getActivitySignature() { + return activitySignature; + } + + @Override + public String getImTemplateCode() { + return imTemplateCode; + } + + public void setImTemplateCode(String imTemplateCode) { + this.imTemplateCode = imTemplateCode; + } + + @Override + public String getTerminalType() { + return terminalType; + } + + public void setTerminalType(String terminalType) { + this.terminalType = terminalType; + } + @Override public String toString() { return getClass() + " - " + type; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventType.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventType.java index 294e25e0e..c4a8d6efb 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventType.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/event/MessagePushEventType.java @@ -20,15 +20,15 @@ public enum MessagePushEventType implements FlowableEventType { */ NOTICE, /** - * 推送待办 + * 推送审批待办 */ PENDING_PUSH, /** - * 恢复待办,异步执行待办任务失败,通知消息恢复待办 + * 恢复审批待办,异步执行待办任务失败,通知消息恢复待办 */ PENDING_ROLLBACK, /** - * 完成待办 + * 完成审批待办 */ PENDING_COMPLETE, /** @@ -43,6 +43,10 @@ public enum MessagePushEventType implements FlowableEventType { * 推送短信 */ SMS, + /** + * IM 消息 + */ + IM, ; public static final MessagePushEventType[] EMPTY_ARRAY = new MessagePushEventType[]{}; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/formhandler/CustomFormFieldHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/formhandler/CustomFormFieldHandler.java new file mode 100644 index 000000000..de17ccaf1 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/formhandler/CustomFormFieldHandler.java @@ -0,0 +1,36 @@ +package cn.axzo.workflow.core.engine.formhandler; + +import org.flowable.content.api.ContentService; +import org.flowable.engine.impl.formhandler.DefaultFormFieldHandler; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.form.api.FormFieldHandler; +import org.flowable.form.api.FormInfo; + +import java.util.Map; + +/** + * 自定义的具体的表单组件类型处理器 + * + * @author wangli + * @since 2025-01-22 13:58 + */ +public class CustomFormFieldHandler extends DefaultFormFieldHandler implements FormFieldHandler { + @Override + public void handleFormFieldsOnSubmit(FormInfo formInfo, + String taskId, + String processInstanceId, + String scopeId, + String scopeType, + Map variables, + String tenantId) { + ContentService contentService = CommandContextUtil.getContentService(); + if (contentService == null || formInfo == null) { + return; + } + } + + @Override + public void enrichFormFields(FormInfo formInfo) { + super.enrichFormFields(formInfo); + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java index c3b73543c..c88d7a782 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java @@ -41,7 +41,7 @@ public class CustomRetryInterceptor extends AbstractCommandInterceptor { // try to execute the command if (AbstractCommand.class.isAssignableFrom(command.getClass())) { // 如果在以后,重试三次也不能解决的话, 可以利用这里的拿到的参数,重新自动构造CMD,并执行. - log.info("Executing command params: {} traceId:{} ", TraceUtil.traceId(), + log.warn("Executing command params: {} traceId:{} ", TraceUtil.traceId(), ((AbstractCommand) command).paramToJsonString()); } return next.execute(config, command, commandExecutor); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceJobHandler.java index ffee64f09..cacbc303f 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceJobHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncAbortProcessInstanceJobHandler.java @@ -1,7 +1,6 @@ package cn.axzo.workflow.core.engine.job; import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceAbortDTO; -import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO; import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceCmd; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.hutool.json.JSONUtil; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskWithFormJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskWithFormJobHandler.java index a3b95f5a9..9c4f36862 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskWithFormJobHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskWithFormJobHandler.java @@ -1,8 +1,6 @@ package cn.axzo.workflow.core.engine.job; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditWithFormDTO; -import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskCmd; import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskWithFormCmd; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBackTaskJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBackTaskJobHandler.java index ec466959c..2c5b642e7 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBackTaskJobHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncBackTaskJobHandler.java @@ -12,6 +12,7 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity; import org.flowable.task.api.Task; import org.flowable.variable.api.delegate.VariableScope; +import java.util.Collections; import java.util.Objects; @Slf4j @@ -35,7 +36,17 @@ public class AsyncBackTaskJobHandler extends AbstractExecuteWithLockJobHandler i if (Objects.isNull(task)) { return; } - processEngineConfiguration.getCommandExecutor().execute(new CustomBackTaskCmd(dto)); + processEngineConfiguration.getCommandExecutor().execute(new CustomBackTaskCmd( + CustomBackTaskCmd.CustomBackParamsDto.builder() + .targetTaskIds(Collections.singletonList(dto.getTaskId())) + .advice(dto.getAdvice()) + .operationDesc(dto.getOperationDesc()) + .attachmentList(dto.getAttachmentList()) + .operator(dto.getApprover()) + .processInstanceId(task.getProcessInstanceId()) + .currentActivityId(task.getTaskDefinitionKey()) + .toActivityId(dto.getToActivityId()) + .validateApprover(true) + .build())); } - } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceJobHandler.java index 52d0baf78..fa171c9fa 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceJobHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncCancelProcessInstanceJobHandler.java @@ -1,6 +1,7 @@ package cn.axzo.workflow.core.engine.job; import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO; import cn.axzo.workflow.core.engine.cmd.CustomCancelProcessInstanceCmd; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.hutool.json.JSONUtil; @@ -34,6 +35,6 @@ public class AsyncCancelProcessInstanceJobHandler extends AbstractJobHandler imp log(job); ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); BpmnProcessInstanceCancelDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnProcessInstanceCancelDTO.class); - processEngineConfiguration.getCommandExecutor().execute(new CustomCancelProcessInstanceCmd(dto, extAxHiTaskInstService)); + processEngineConfiguration.getCommandExecutor().execute(new CustomCancelProcessInstanceCmd((SuperBpmnProcessInstanceCancelDTO) dto, extAxHiTaskInstService)); } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncRemindTaskJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncRemindTaskJobHandler.java new file mode 100644 index 000000000..e423f204b --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncRemindTaskJobHandler.java @@ -0,0 +1,45 @@ +package cn.axzo.workflow.core.engine.job; + +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO; +import cn.axzo.workflow.core.conf.SupportRefreshProperties; +import cn.axzo.workflow.core.engine.cmd.CustomRemindTaskCmd; +import cn.hutool.json.JSONUtil; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.job.service.JobHandler; +import org.flowable.job.service.impl.persistence.entity.JobEntity; +import org.flowable.variable.api.delegate.VariableScope; + +/** + * 异步的催办任务的处理器 + * + * @author wangli + * @since 2025-06-13 14:13 + */ +@Slf4j +public class AsyncRemindTaskJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler { + public static final String TYPE = "async-remind-task"; + private final SupportRefreshProperties refreshProperties; + + public AsyncRemindTaskJobHandler(SupportRefreshProperties refreshProperties) { + this.refreshProperties = refreshProperties; + } + + @Override + public String getType() { + return TYPE; + } + + @Override + public void executeInternal(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) { + log.info("AsyncRejectTaskJobHandler executing..."); + log(job); + ProcessEngineConfigurationImpl processEngineConfiguration = + CommandContextUtil.getProcessEngineConfiguration(commandContext); + BpmnTaskRemindDTO dto = JSONUtil.toBean(job.getCustomValues(), BpmnTaskRemindDTO.class); + processEngineConfiguration.getCommandExecutor().execute(new CustomRemindTaskCmd(dto.getTerminalType(), + dto.getProcessInstanceId(), dto.getTaskDefinitionKey(), dto.getRemindTypes(), refreshProperties)); + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncTermNodeAlterJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncTermNodeAlterJobHandler.java index 941304222..192da572c 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncTermNodeAlterJobHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncTermNodeAlterJobHandler.java @@ -1,14 +1,18 @@ package cn.axzo.workflow.core.engine.job; +import cn.axzo.basics.common.util.NumberUtil; import cn.axzo.workflow.common.model.dto.AlterDTO; import cn.axzo.workflow.common.model.dto.TermNodePausingDTO; import cn.axzo.workflow.core.common.utils.SpringContextUtils; import cn.axzo.workflow.core.conf.SupportRefreshProperties; +import cn.axzo.workflow.core.engine.tx.listener.DeleteTimerJobTransactionListener; import cn.axzo.workflow.core.listener.Alter; import cn.hutool.core.date.DateUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.impl.cfg.TransactionState; +import org.flowable.common.engine.impl.context.Context; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; @@ -18,8 +22,10 @@ import org.flowable.job.service.JobHandler; import org.flowable.job.service.impl.persistence.entity.JobEntity; import org.flowable.task.api.Task; import org.flowable.variable.api.delegate.VariableScope; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; -import java.util.Objects; +import java.util.List; import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_NODE_ALTER; @@ -45,7 +51,7 @@ public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements @Override public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) { - log.warn("AsyncActivityLeaveJobHandler exec start..."); + log.warn("AsyncTermNodeAlterJobHandler exec start..."); ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); JSONObject jsonObject = JSON.parseObject(job.getJobHandlerConfiguration()); if (!jsonObject.containsKey("activityId")) { @@ -55,25 +61,71 @@ public class AsyncTermNodeAlterJobHandler extends AbstractJobHandler implements RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); TermNodePausingDTO dto = runtimeService.getVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, TermNodePausingDTO.class); TaskService taskService = processEngineConfiguration.getTaskService(); - Task task = taskService.createTaskQuery() + List tasks = taskService.createTaskQuery() .processInstanceId(dto.getProcessInstanceId()) .taskDefinitionKey(dto.getActivityId()) - .singleResult(); - if (Objects.isNull(task)) { + .active() + .list(); + + StringBuilder sb = new StringBuilder(); + tasks.forEach(e -> { + sb.append("id:").append(e.getId()).append(", assignee: ").append(e.getAssignee()); + }); + log.info("tasks size:{}", JSON.toJSONString(sb)); + if (CollectionUtils.isEmpty(tasks) || tasks.size() > 1 || hasAssignee(tasks.get(0).getAssignee())) { + deleteTimerJob(dto); return; } - if (DateUtil.compare(DateUtil.date(), DateUtil.offsetMinute(task.getCreateTime(), refreshProperties.getPauseDelay())) > 0) { + if (DateUtil.compare(DateUtil.date(), DateUtil.offsetMinute(tasks.get(0).getCreateTime(), refreshProperties.getPauseDelay())) <= 0) { + return; + } + + // 不允许重复告警 + if (!refreshProperties.getRepeatAlter() && dto.getRetries() > 0) { + deleteTimerJob(dto); + return; + } + + + if (refreshProperties.getAlterRetries() == 0 || dto.getRetries() < refreshProperties.getAlterRetries()) { // 发送告警对象 Alter alter = SpringContextUtils.getBean(Alter.class); AlterDTO alterDTO = new AlterDTO(); alterDTO.setProcessInstanceId(dto.getProcessInstanceId()); alterDTO.setActivityId(dto.getActivityId()); - alterDTO.setTaskId(task.getId()); - alterDTO.setStartTime(task.getCreateTime()); - alterDTO.setPrettyStartTime(DateUtil.formatDateTime(task.getCreateTime())); - if (Boolean.TRUE.equals(refreshProperties.getSendDingTalk())) { + alterDTO.setTaskId(tasks.get(0).getId()); + alterDTO.setStartTime(tasks.get(0).getCreateTime()); + alterDTO.setPrettyStartTime(DateUtil.formatDateTime(tasks.get(0).getCreateTime())); + if (Boolean.TRUE.equals(refreshProperties.getAlterSendDingTalk())) { alter.invoke(alterDTO); + + // 记录告警次数 + incRetries(job, dto, runtimeService, activityId); + + if (refreshProperties.getAlterRetries() != 0 && dto.getRetries() >= refreshProperties.getAlterRetries()) { + deleteTimerJob(dto); + } } } } + + private void incRetries(JobEntity job, TermNodePausingDTO dto, RuntimeService runtimeService, String activityId) { + dto.setRetries(dto.getRetries() + 1); + runtimeService.setVariable(job.getProcessInstanceId(), BIZ_NODE_ALTER + activityId, dto); + } + + private void deleteTimerJob(TermNodePausingDTO dto) { + Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED, + new DeleteTimerJobTransactionListener(dto)); + } + + private Boolean hasAssignee(String assignee) { + if (!StringUtils.hasText(assignee)) { + return false; + } + String[] split = assignee.split("\\|"); + return split.length == 2 && NumberUtil.isPositiveNumber(Long.valueOf(split[1])); + } + + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/service/CustomTimerJobEntityManagerImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/service/CustomTimerJobEntityManagerImpl.java new file mode 100644 index 000000000..413c0563e --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/service/CustomTimerJobEntityManagerImpl.java @@ -0,0 +1,26 @@ +package cn.axzo.workflow.core.engine.job.service; + +import org.flowable.job.service.JobServiceConfiguration; +import org.flowable.job.service.impl.persistence.entity.JobEntity; +import org.flowable.job.service.impl.persistence.entity.TimerJobEntity; +import org.flowable.job.service.impl.persistence.entity.TimerJobEntityManagerImpl; +import org.flowable.job.service.impl.persistence.entity.data.TimerJobDataManager; + +/** + * + * @author wangli + * @since 2025-03-12 14:14 + */ +public class CustomTimerJobEntityManagerImpl extends TimerJobEntityManagerImpl { + public CustomTimerJobEntityManagerImpl(JobServiceConfiguration jobServiceConfiguration, TimerJobDataManager jobDataManager) { + super(jobServiceConfiguration, jobDataManager); + } + + @Override + protected TimerJobEntity createTimer(JobEntity te) { + TimerJobEntity timer = super.createTimer(te); + timer.setElementId(te.getElementId()); + timer.setElementName(te.getElementName()); + return timer; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/service/JobLogProcessor.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/service/JobLogProcessor.java index cc6282efc..7e82dec55 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/service/JobLogProcessor.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/service/JobLogProcessor.java @@ -3,10 +3,7 @@ package cn.axzo.workflow.core.engine.job.service; import cn.axzo.workflow.common.constant.LogFieldConstants; import cn.axzo.workflow.core.engine.job.utils.AsyncJobUtils; import cn.azxo.framework.common.constatns.Constants; -import cn.hutool.json.JSONUtil; -import lombok.Data; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.flowable.job.service.JobProcessor; import org.flowable.job.service.JobProcessorContext; import org.flowable.job.service.impl.persistence.entity.AbstractJobEntity; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java index 34f4b020e..dce16737d 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java @@ -3,7 +3,6 @@ package cn.axzo.workflow.core.engine.listener; import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO; -import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; import cn.axzo.workflow.core.engine.cmd.CustomCarbonCopyUserSelectorCmd; import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder; import cn.axzo.workflow.core.engine.event.MessagePushEventImpl; @@ -37,7 +36,9 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCarbonCopyConfigs; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNoticeConfig; import static cn.axzo.workflow.core.listener.AbstractBpmnEventListener.parseProcessDefinitionKey; /** @@ -89,12 +90,12 @@ public class EngineCarbonCopyEventListener implements JavaDelegate { INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId, carbonUsers); // 发送抄送事件 - invokeCarbonCopy(carbonUsers, BpmnMetaParserHelper.getNoticeConfig(mainProcess), execution); + invokeCarbonCopy(carbonUsers, getNoticeConfig(mainProcess), getActivitySignature(serviceTask), execution); } private void invokeCarbonCopy(List carbonUsers, Optional noticeConf, - DelegateExecution execution) { + Boolean activitySignature, DelegateExecution execution) { if (CollectionUtils.isEmpty(carbonUsers)) { return; } @@ -111,7 +112,7 @@ public class EngineCarbonCopyEventListener implements JavaDelegate { MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.CARBON_COPY, carbonUsers, bpmnNoticeConf, execution.getProcessInstanceId(), parseProcessDefinitionKey(execution.getProcessDefinitionId()), - execution.getTenantId(), getCarbonTaskId(execution)); + execution.getTenantId(), getCarbonTaskId(execution), activitySignature); eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey()); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java index 433de5f05..ad5c9a3db 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java @@ -34,12 +34,31 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; -import static cn.axzo.workflow.common.constant.BpmnConstants.*; -import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.*; +import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ASSIGNER_LIMIT_NUMBER; +import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_ORG_RELATION; +import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_ALLOW_SKIP_USER_TASK; +import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE; +import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_TYPE; +import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_SIGN; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK; import static cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner.buildDummyAssigner; -import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.*; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverEmptyHandleType; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNodeType; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getProcessServerVersion; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.getLimitedElementList; import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.removeDuplicateByPersonId; @@ -91,8 +110,11 @@ public class EngineExecutionStartListener implements ExecutionListener { Optional nodeType = getNodeType(userTask); DefaultArtifactVersion supportVersion = new DefaultArtifactVersion(FLOW_SERVER_VERSION_121); DefaultArtifactVersion currentVersion = new DefaultArtifactVersion(processServerVersion); + + List supportedNodeType = Lists.newArrayList(NODE_TASK, NODE_BUSINESS, NODE_SIGN, NODE_STARTER); + if (currentVersion.compareTo(supportVersion) >= 0 && nodeType.isPresent() - && (Objects.equals(NODE_TASK, nodeType.get()) || Objects.equals(NODE_BUSINESS, nodeType.get()) || Objects.equals(NODE_STARTER, nodeType.get()))) { + && (supportedNodeType.contains(nodeType.get()))) { if (Objects.equals(NODE_STARTER, nodeType.get())) { // UserTask 多实例, 该变量用于引擎 BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(execution.getVariable(INTERNAL_INITIATOR)); @@ -143,6 +165,8 @@ public class EngineExecutionStartListener implements ExecutionListener { // UserTask 多实例, 该变量用于引擎 execution.setVariable(assigneeListVariableName, assigneeIdList); }); + } else { + log.error("current version: {} 过低,本地运行则需先 maven package,服务端运行则需排查逻辑", currentVersion); } } @@ -171,12 +195,13 @@ public class EngineExecutionStartListener implements ExecutionListener { case transferToAdmin: assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.transferToAdmin.getType(), userTask, execution, true)); - finalEmptyAssigneeHandle(assigners, userTask, execution, "转交管理员失败,系统中止"); + + finalEmptyAssigneeHandle(assigners, userTask, execution, "未找到审批人且转交管理员失败,自动中止", "karma: " + (refreshProperties.getUseNewToAdminApi() ? "api/flow/listTaskAssignerAdmin/v2" : "api/flow/listTaskAssignerAdmin")); break; case specifyAssignee: assigners.addAll(approverSelect(ApproverEmptyHandleTypeEnum.specifyAssignee.getType(), userTask, execution, true)); - finalEmptyAssigneeHandle(assigners, userTask, execution, "转交指定人员失败,系统自动中止"); + finalEmptyAssigneeHandle(assigners, userTask, execution, "转交指定人员失败,自动中止", "org-gateway: api/node-user/list"); break; default: break; @@ -185,12 +210,12 @@ public class EngineExecutionStartListener implements ExecutionListener { }); } - private void finalEmptyAssigneeHandle(List assigners, UserTask userTask, DelegateExecution execution, String operationDesc) { + private void finalEmptyAssigneeHandle(List assigners, UserTask userTask, DelegateExecution execution, String operationDesc, String targetUrl) { if (CollectionUtils.isEmpty(assigners)) { //发送钉钉消息 if (Boolean.TRUE.equals(refreshProperties.getSendDingTalk())) { CooperationOrgDTO orgScopes = execution.getVariable(BIZ_ORG_RELATION, CooperationOrgDTO.class); - DingTalkUtils.sendDingTalkForTransferToAdminError(profile, execution.getProcessInstanceId(), userTask.getId(), orgScopes); + DingTalkUtils.sendDingTalkForTransferToAdminError(profile, execution.getProcessInstanceId(), userTask.getId(), orgScopes, targetUrl); } BpmnProcessInstanceAbortDTO abortDTO = new BpmnProcessInstanceAbortDTO(); abortDTO.setProcessInstanceId(execution.getProcessInstanceId()); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineNoticeEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineNoticeEventListener.java index 7c7bf6229..9aae50069 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineNoticeEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineNoticeEventListener.java @@ -19,6 +19,7 @@ import java.util.Set; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.CARBON_COPY; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.CARBON_COPY_COMPLETE; +import static cn.axzo.workflow.core.engine.event.MessagePushEventType.IM; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.NOTICE; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.PENDING_COMPLETE; import static cn.axzo.workflow.core.engine.event.MessagePushEventType.PENDING_PUSH; @@ -47,6 +48,7 @@ public class EngineNoticeEventListener extends AbstractFlowableEventListener { .add(CARBON_COPY) .add(CARBON_COPY_COMPLETE) .add(SMS) + .add(IM) .build(); @Override @@ -101,6 +103,12 @@ public class EngineNoticeEventListener extends AbstractFlowableEventListener { stopWatch.stop(); log.info("SMS StopWatch : running time = {} 's", stopWatch.getTotalTimeSeconds()); break; + case IM: + stopWatch.start("IM Event Execution Time"); + getOrderedListeners().forEach(i -> i.onIm(event)); + stopWatch.stop(); + log.info("IM StopWatch : running time = {} 's", stopWatch.getTotalTimeSeconds()); + break; default: } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineProcessInstanceEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineProcessInstanceEventListener.java index 25aa3d6ba..f8c3ba7b3 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineProcessInstanceEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineProcessInstanceEventListener.java @@ -159,7 +159,7 @@ public class EngineProcessInstanceEventListener extends AbstractFlowableEngineEv serviceVersion, workspaceType, config.getNotice(), event.getProcessInstanceId(), assigner)); MessagePushEventImpl messagePushEvent = MessagePushEventBuilder.createEvent(NOTICE, assigners, config, processInstance.getProcessInstanceId(), processInstance.getProcessDefinitionKey(), - processInstance.getTenantId(), null); + processInstance.getTenantId(), null, false); log.info("发送通知消息: {}", JSONUtil.toJsonStr(messagePushEvent)); eventDispatcher.dispatchEvent(messagePushEvent, processEngineConfiguration.getEngineCfgKey()); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineTaskEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineTaskEventListener.java index 6bf3bdc56..f48f69287 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineTaskEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineTaskEventListener.java @@ -14,6 +14,9 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static cn.axzo.workflow.common.constant.TaskListenerExtConstants.EVENTNAME_FALLBACK; +import static cn.axzo.workflow.common.constant.TaskListenerExtConstants.EVENTNAME_TRANSFER; + /** * 引擎用户任务事件监听, 用于 BPMN 文件中为节点设置指定类型监听 * @@ -54,6 +57,12 @@ public class EngineTaskEventListener implements TaskListener { // 审批 i.onDeleted(delegateTask); break; + case EVENTNAME_TRANSFER: + i.onTransfer(delegateTask); + break; + case EVENTNAME_FALLBACK: + i.onFallback(delegateTask); + break; default: } }); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/TaskEntityEventHandle.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/TaskEntityEventHandle.java index 402f66030..41c30fd3c 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/TaskEntityEventHandle.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/entity/type/TaskEntityEventHandle.java @@ -30,12 +30,24 @@ import java.util.Date; import java.util.List; import java.util.Objects; -import static cn.axzo.workflow.common.constant.BpmnConstants.*; +import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION; +import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE; +import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC; +import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO; +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE; +import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.nobody; -import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.*; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.AND; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.GENERAL; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.OR; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; -import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.*; -import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.PENDING; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; +import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.HANDLING; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNodeType; @@ -81,9 +93,10 @@ public class TaskEntityEventHandle implements EntityEventHandle { log.setNodeMode((isNodeStarter ? BpmnFlowNodeMode.GENERAL : getNodeMode(flowElement)).getType()); log.setTaskId(taskEntity.getId()); String operationDesc = taskEntity.getVariable(COMMENT_TYPE_OPERATION_DESC, String.class); - log.setOperationDesc(StringUtils.hasText(operationDesc) ? operationDesc : PENDING.getDesc()); + log.setOperationDesc(StringUtils.hasText(operationDesc) ? operationDesc : HANDLING.getDesc()); log.setStartTime(taskEntity.getCreateTime()); log.setStatus(PROCESSING.getStatus()); + log.setSignature(getActivitySignature(flowElement)); processLogService.insert(log); } @@ -112,6 +125,7 @@ public class TaskEntityEventHandle implements EntityEventHandle { } else { ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); + @SuppressWarnings("unchecked") List assigneeList = runtimeService.getVariable(taskEntity.getProcessInstanceId(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey(), List.class); ListUtils.emptyIfNull(assigneeList).stream().filter(e -> Objects.equals(e.buildAssigneeId(), taskEntity.getAssignee())).findAny() @@ -129,7 +143,7 @@ public class TaskEntityEventHandle implements EntityEventHandle { ExtAxProcessLog queryLog = new ExtAxProcessLog(); queryLog.setProcessInstanceId(taskEntity.getProcessInstanceId()); queryLog.setTaskId(taskEntity.getId()); - queryLog.setOperationDesc(PENDING.getDesc()); + queryLog.setOperationDesc(HANDLING.getDesc()); ExtAxProcessLog update = new ExtAxProcessLog(); ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); @@ -141,6 +155,7 @@ public class TaskEntityEventHandle implements EntityEventHandle { update.setAssigneeTenantId(assignee.getTenantId()); update.setAssigneeName(assignee.getAssignerName()); update.setAssigneeOuId(assignee.getOuId()); + update.setOperationDesc(assignee.getAssignerName()); } boolean needDelete = false; @@ -178,6 +193,7 @@ public class TaskEntityEventHandle implements EntityEventHandle { // 判断是否抄送节点,如果是的话,需要将抄送人集合放入对应字段 if (isCarbonCopyNode(queryLog)) { // 抄送人集合 + @SuppressWarnings("unchecked") List carbonCopies = runtimeService.getVariable(taskEntity.getProcessInstanceId(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey(), List.class); update.setAssigneeFull(carbonCopies); update.setOperationDesc("抄送" + carbonCopies.size() + "人"); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java index d1e14ba0b..3fd368e85 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/operation/DeleteProcessInstanceOperation.java @@ -2,13 +2,16 @@ package cn.axzo.workflow.core.engine.operation; import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; +import lombok.extern.slf4j.Slf4j; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.engine.HistoryService; +import org.flowable.engine.ManagementService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.impl.agenda.AbstractOperation; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.job.api.Job; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -25,6 +28,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_CLOSING_TYP * @author wangli * @since 2024/1/3 20:03 */ +@Slf4j public class DeleteProcessInstanceOperation extends AbstractOperation { private final String processInstanceId; private final ExtAxHiTaskInstService extAxHiTaskInstService; @@ -70,10 +74,19 @@ public class DeleteProcessInstanceOperation extends AbstractOperation { } + ManagementService managementService = processEngineConfiguration.getManagementService(); + List timerJobs = managementService.createTimerJobQuery().processInstanceId(processInstanceId).list(); + if (!CollectionUtils.isEmpty(timerJobs)) { + log.warn("timerjobs size: {}, processInstanceId: {}", timerJobs.size(), processInstanceId); + timerJobs.forEach(e -> managementService.deleteTimerJob(e.getId())); + } + RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); runtimeService.setVariableLocal(processInstanceId, PROCESS_CLOSING_TYPE, closingType); runtimeService.deleteProcessInstance(processInstanceId, StringUtils.hasText(customDeleteReason) ? customDeleteReason : HIDDEN_ASSIGNEE_ID); // 将结束流程实例的原因记录下来 // runtimeService.deleteProcessInstance(processInstanceId, reason); + + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AddTimerJobTransactionListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AddTimerJobTransactionListener.java new file mode 100644 index 000000000..92612305f --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AddTimerJobTransactionListener.java @@ -0,0 +1,31 @@ +package cn.axzo.workflow.core.engine.tx.listener; + +import cn.axzo.workflow.common.model.dto.TermNodeAddTimerJobDTO; +import cn.axzo.workflow.core.engine.cmd.CustomAddTimerJobCmd; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.impl.cfg.TransactionListener; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; + +/** + * 删除业务节点长时间卡住的定时处理器 + * + * @author wangli + * @since 2025-03-12 15:02 + */ +@Slf4j +@AllArgsConstructor +public class AddTimerJobTransactionListener implements TransactionListener { + private final TermNodeAddTimerJobDTO dto; + + @Override + public void execute(CommandContext commandContext) { + log.info("add timer job listener. instanceId: {}, activityId: {}, timeCycle: {}", dto.getProcessInstanceId(), dto.getActivityId(), dto.getTimeCycle()); + ProcessEngineConfigurationImpl processEngineConfiguration = + CommandContextUtil.getProcessEngineConfiguration(commandContext); + processEngineConfiguration.getCommandExecutor().execute(new CustomAddTimerJobCmd(dto)); + + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java index f6f7b4259..7a12c474a 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoPassTransactionListener.java @@ -41,6 +41,7 @@ import static org.flowable.common.engine.impl.interceptor.EngineConfigurationCon public class AutoPassTransactionListener implements TransactionListener { private final DelegateTask delegateTask; private final String advice; + private final String operationDesc; @Override public void execute(CommandContext commandContext) { @@ -59,7 +60,7 @@ public class AutoPassTransactionListener implements TransactionListener { pass.setTaskId(delegateTask.getId()); pass.setAdvice(advice); pass.setApprover(assigner); - pass.setOperationDesc("(自动通过)"); + pass.setOperationDesc(operationDesc); String jobId = commandExecutor.execute(commandConfig, new CustomApproveTaskAsyncCmd(pass)); // 重置任务,因为上面的 cmd 和这个 cmd 的 lock 对象不一致 diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoRejectTransactionListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoRejectTransactionListener.java index b34d8c28a..4cd8bcbe6 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoRejectTransactionListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/AutoRejectTransactionListener.java @@ -33,6 +33,7 @@ import static org.flowable.common.engine.impl.interceptor.EngineConfigurationCon @AllArgsConstructor public class AutoRejectTransactionListener implements TransactionListener { private final DelegateTask delegateTask; + private final String operationDesc; @Override public void execute(CommandContext commandContext) { @@ -46,8 +47,8 @@ public class AutoRejectTransactionListener implements TransactionListener { CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); BpmnTaskAuditDTO reject = new BpmnTaskAuditDTO(); reject.setTaskId(delegateTask.getId()); - reject.setApprover(new BpmnTaskDelegateAssigner("系统", "system", delegateTask.getTenantId())); - reject.setOperationDesc("自动驳回"); + reject.setApprover(new BpmnTaskDelegateAssigner("", "system", delegateTask.getTenantId())); + reject.setOperationDesc(operationDesc); String jobId = commandExecutor.execute(commandConfig, new CustomRejectionTaskAsyncCmd(reject)); // 重置任务,因为上面的 cmd 和这个 cmd 的 lock 对象不一致 diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/DeleteTimerJobTransactionListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/DeleteTimerJobTransactionListener.java new file mode 100644 index 000000000..402ffb794 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/tx/listener/DeleteTimerJobTransactionListener.java @@ -0,0 +1,42 @@ +package cn.axzo.workflow.core.engine.tx.listener; + +import cn.axzo.workflow.common.model.dto.TermNodePausingDTO; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.impl.cfg.TransactionListener; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.engine.ManagementService; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.job.api.Job; +import org.flowable.job.service.impl.cmd.DeleteTimerJobCmd; + +import java.util.Objects; + +/** + * 删除业务节点长时间卡住的定时处理器 + * + * @author wangli + * @since 2025-03-12 15:02 + */ +@Slf4j +@AllArgsConstructor +public class DeleteTimerJobTransactionListener implements TransactionListener { + private final TermNodePausingDTO dto; + + @Override + public void execute(CommandContext commandContext) { + log.info("clear timer job. instanceId: {}, activityId: {}", dto.getProcessInstanceId(), dto.getActivityId()); + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); + ManagementService managementService = processEngineConfiguration.getManagementService(); + Job timerJob = managementService.createTimerJobQuery() + .elementId(dto.getActivityId()) + .processInstanceId(dto.getProcessInstanceId()) + .singleResult(); + if (Objects.nonNull(timerJob)) { + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + commandExecutor.execute(new DeleteTimerJobCmd(timerJob.getId(), processEngineConfiguration.getJobServiceConfiguration())); + } + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/AbstractBpmnEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/AbstractBpmnEventListener.java index 8a9c3aae3..de1d2069e 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/AbstractBpmnEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/AbstractBpmnEventListener.java @@ -17,11 +17,13 @@ public abstract class AbstractBpmnEventListener impl private T context; + @SuppressWarnings("unchecked") @Override public void setContext(OperationContext context) { this.context = (T) context; } + @SuppressWarnings("unchecked") @Override public T getContext() { return context; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnMessagePushEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnMessagePushEventListener.java index 85ad4d670..bdc30abd7 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnMessagePushEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnMessagePushEventListener.java @@ -38,6 +38,7 @@ public interface BpmnMessagePushEventListener extends OperationContext, Ordered /** * 恢复待办 + * * @param event */ default void onPendingRollback(MessagePushEvent event) { @@ -67,4 +68,12 @@ public interface BpmnMessagePushEventListener extends OperationContext, Ordered */ default void onSms(MessagePushEvent event) { } + + /** + * 推送即时消息 + * + * @param event + */ + default void onIm(MessagePushEvent event) { + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnTaskEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnTaskEventListener.java index fc4959e58..1437a0970 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnTaskEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/BpmnTaskEventListener.java @@ -8,7 +8,7 @@ import org.springframework.core.Ordered; * 流程审批任务相关事件, 供外部引入 jar 包方式使用的钩子 *

* 微服版本使用, 统一在 Server module 中进行实现扩展, 不在该 module 中实现. - * + *

* 特别注意: 在 Flowable Engine 中 onAssigned 执行优先级比 onCreated 更高 * * @author shao_hua @@ -18,25 +18,47 @@ public interface BpmnTaskEventListener extends OperationContext, Ordered { /** * 用户任务已指派审核人 */ - default void onAssigned(DelegateTask delegateTask) {} + default void onAssigned(DelegateTask delegateTask) { + } /** * 用户任务已创建,未指派审核人 */ - default void onCreated(DelegateTask delegateTask) {} + default void onCreated(DelegateTask delegateTask) { + } /** * 用户任务已通过 *

* 仅审核通过一个用户任务时触发, 如果任务是驳回了, 则直接走实例撤回事件 */ - default void onCompleted(DelegateTask delegateTask) {} + default void onCompleted(DelegateTask delegateTask) { + } /** * 用户任务已删除 - * + *

* 删除不代表驳回或拒绝,因为通过也会走该事件 */ - default void onDeleted(DelegateTask delegateTask) {} + default void onDeleted(DelegateTask delegateTask) { + } + + /** + * 用户任务已转交 + * + * @param delegateTask + * @since 1.5.2 临时新增的动作类的事件 + */ + default void onTransfer(DelegateTask delegateTask) { + } + + /** + * 用户任务已回退 + * + * @param delegateTask + * @since 1.5.2 临时新增的动作类的事件 + */ + default void onFallback(DelegateTask delegateTask) { + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/impl/InternalBpmnActivityEventListener_lo_Listener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/impl/InternalBpmnActivityEventListener_lo_Listener.java index e62f2d584..b64841110 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/impl/InternalBpmnActivityEventListener_lo_Listener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/listener/impl/InternalBpmnActivityEventListener_lo_Listener.java @@ -1,29 +1,26 @@ package cn.axzo.workflow.core.listener.impl; import cn.axzo.workflow.common.enums.BpmnFlowNodeType; +import cn.axzo.workflow.common.model.dto.TermNodeAddTimerJobDTO; import cn.axzo.workflow.common.model.dto.TermNodePausingDTO; import cn.axzo.workflow.core.common.context.ActivityOperationContext; import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; import cn.axzo.workflow.core.conf.SupportRefreshProperties; -import cn.axzo.workflow.core.engine.job.AsyncTermNodeAlterJobHandler; +import cn.axzo.workflow.core.engine.tx.listener.AddTimerJobTransactionListener; +import cn.axzo.workflow.core.engine.tx.listener.DeleteTimerJobTransactionListener; import cn.axzo.workflow.core.listener.AbstractBpmnEventListener; import cn.axzo.workflow.core.listener.BpmnActivityEventListener; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.FlowElement; -import org.flowable.bpmn.model.TimerEventDefinition; -import org.flowable.engine.ManagementService; +import org.flowable.common.engine.impl.cfg.TransactionState; +import org.flowable.common.engine.impl.context.Context; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; -import org.flowable.engine.impl.jobexecutor.TimerEventHandler; -import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.util.CommandContextUtil; import org.flowable.engine.impl.util.ProcessDefinitionUtil; -import org.flowable.engine.impl.util.TimerUtil; -import org.flowable.job.api.Job; -import org.flowable.job.service.impl.persistence.entity.TimerJobEntity; import org.springframework.context.annotation.Scope; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; @@ -66,7 +63,7 @@ public class InternalBpmnActivityEventListener_lo_Listener extends AbstractBpmnE } ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); RuntimeService runtimeService = processEngineConfiguration.getRuntimeService(); - TermNodePausingDTO dto = new TermNodePausingDTO(execution.getProcessInstanceId(), execution.getCurrentActivityId(), refreshProperties.getAlterRetries()); + TermNodePausingDTO dto = new TermNodePausingDTO(execution.getProcessInstanceId(), execution.getCurrentActivityId(), 0); runtimeService.setVariable(execution.getProcessInstanceId(), BIZ_NODE_ALTER + execution.getCurrentActivityId(), dto); BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(execution.getProcessDefinitionId()); FlowElement flowElement = bpmnModel.getFlowElement(execution.getCurrentActivityId()); @@ -76,32 +73,28 @@ public class InternalBpmnActivityEventListener_lo_Listener extends AbstractBpmnE switch (method) { case nobody: case bizSpecify: - // 业务指定审批人,需要在业务设置了人后,清除定时 - TimerEventDefinition timerEventDefinition = new TimerEventDefinition(); + // 业务指定审批人,需要在业务设置了人后,设置定时 String timeUnit; switch (refreshProperties.getAlterIntervalUnit()) { - case SECONDS: - timeUnit = "S"; + case MINUTES: + timeUnit = "M"; break; case HOURS: timeUnit = "H"; break; - case DAYS: - timeUnit = "D"; - break; default: - // MINUTES - timeUnit = "M"; + timeUnit = "S"; break; - } - timerEventDefinition.setTimeCycle("R" + refreshProperties.getAlterRetries() + "/PT" + refreshProperties.getAlterInterval() + timeUnit); - TimerJobEntity timerJob = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition, execution.getCurrentFlowElement(), - false, (ExecutionEntity) execution, AsyncTermNodeAlterJobHandler.TYPE, - TimerEventHandler.createConfiguration(execution.getCurrentActivityId(), null, timerEventDefinition.getCalendarName())); - if (timerJob != null) { - CommandContextUtil.getTimerJobService().scheduleTimerJob(timerJob); } + + TermNodeAddTimerJobDTO addTimerJobDTO = new TermNodeAddTimerJobDTO(); + addTimerJobDTO.setProcessInstanceId(execution.getProcessInstanceId()); + addTimerJobDTO.setActivityId(execution.getCurrentActivityId()); + addTimerJobDTO.setTimeCycle("R100/PT" + refreshProperties.getAlterInterval() + timeUnit); + + Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED, + new AddTimerJobTransactionListener(addTimerJobDTO)); break; default: break; @@ -118,14 +111,21 @@ public class InternalBpmnActivityEventListener_lo_Listener extends AbstractBpmnE */ @Override public void onEnd(DelegateExecution execution) { - ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); - ManagementService managementService = processEngineConfiguration.getManagementService(); - Job timerJob = managementService.createTimerJobQuery() - .elementId(execution.getCurrentActivityId()) - .processInstanceId(execution.getProcessInstanceId()) - .singleResult(); - if (Objects.nonNull(timerJob)) { - CommandContextUtil.getTimerJobService().deleteTimerJob((TimerJobEntity) timerJob); - } +// ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); +// ManagementService managementService = processEngineConfiguration.getManagementService(); +// Job timerJob = managementService.createTimerJobQuery() +// .elementId(execution.getCurrentActivityId()) +// .processInstanceId(execution.getProcessInstanceId()) +// .singleResult(); +// if (Objects.nonNull(timerJob)) { +// CommandContextUtil.getTimerJobService().deleteTimerJob((TimerJobEntity) timerJob); +// } + + TermNodePausingDTO dto = new TermNodePausingDTO(); + dto.setActivityId(execution.getCurrentActivityId()); + dto.setProcessInstanceId(execution.getProcessInstanceId()); + dto.setRetries(1); + Context.getTransactionContext().addTransactionListener(TransactionState.COMMITTED, + new DeleteTimerJobTransactionListener(dto)); } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/mq/CustomRocketMQEventProducer.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/mq/CustomRocketMQEventProducer.java index fb56caa43..7f57bb07f 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/mq/CustomRocketMQEventProducer.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/mq/CustomRocketMQEventProducer.java @@ -9,6 +9,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.ListUtils; import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.BeanUtils; @@ -68,8 +69,7 @@ public class CustomRocketMQEventProducer extends RocketMQEventProducer { newHeaders.put(TraceUtils.TRACE_ID, TraceUtils.getOrCreateTraceId()); newHeaders.put(TraceUtils.CTX_LOG_ID, TraceUtils.getOrCreateTraceId()); newHeaders.put(TraceUtils.TRACE_ID_IN_MDC, TraceUtils.getOrCreateTraceId()); -// newHeaders.put(MQ_OWNERSHIP_APPLICATION, applicationName); - newHeaders.put(MQ_OWNERSHIP_APPLICATION, "senna"); + newHeaders.put(MQ_OWNERSHIP_APPLICATION, applicationName); final Context copiedContext = context.toBuilder().headers(newHeaders).build(); Runnable runnable = () -> { @@ -99,7 +99,7 @@ public class CustomRocketMQEventProducer extends RocketMQEventProducer { } List runnables = getAfterCommitExecutor().getRunnables(); - log.info("runnables.size(): {}", runnables.size()); + log.info("runnables.size(): {}", ListUtils.emptyIfNull(runnables).size()); } @Override diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDict.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDict.java index 6ae09bcc3..3e68f493f 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDict.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDict.java @@ -1,6 +1,7 @@ package cn.axzo.workflow.core.repository.entity; import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import cn.axzo.workflow.common.enums.BusinessTypeEnum; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -12,6 +13,7 @@ import lombok.ToString; @Data @ToString(callSuper = true) public class ExtAxDict extends BaseEntity { + private static final long serialVersionUID = 720139149636794190L; /** * 编号,自增 */ @@ -62,4 +64,19 @@ public class ExtAxDict extends BaseEntity { * 工作台类型值 */ private String workspaceTypeCode; + + /** + * 图标 + */ + private String icon; + + /** + * 业务类型 + */ + private BusinessTypeEnum businessType; + + /** + * 是否展示在发起工作台 + */ + private Boolean displayInitiateMenu; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictConf.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictConf.java index 960f38a05..23604601b 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictConf.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictConf.java @@ -18,6 +18,8 @@ import lombok.ToString; @ToString(callSuper = true) public class ExtAxDictConf extends BaseEntity { + private static final long serialVersionUID = 4100924919539718197L; + /** * 字典 ID */ diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictGroup.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictGroup.java new file mode 100644 index 000000000..355944e03 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictGroup.java @@ -0,0 +1,36 @@ +package cn.axzo.workflow.core.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode(callSuper = true) +@TableName(value = "ext_ax_dict_group", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ExtAxDictGroup extends BaseEntity { + + private static final long serialVersionUID = 7133194071098740752L; + + /** + * 对应字典id + */ + private Long dictId; + + /** + * 上级id + */ + private Long parentGroupId; + + /** + * 分组名 + */ + private String groupName; + + /** + * 顺序 + */ + private Integer ordinal; +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictGroupVariable.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictGroupVariable.java new file mode 100644 index 000000000..8fce15f7a --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDictGroupVariable.java @@ -0,0 +1,47 @@ +package cn.axzo.workflow.core.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import cn.axzo.workflow.common.enums.VarTypeEnum; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@EqualsAndHashCode(callSuper = true) +@TableName(value = "ext_ax_dict_group_variable", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ExtAxDictGroupVariable extends BaseEntity { + + private static final long serialVersionUID = 2910796107844751820L; + + /** + * 字典id + */ + private Long dictId; + + /** + * 分组id + */ + private Long groupId; + + /** + * 变量类型,文本/图片 + */ + private VarTypeEnum type; + + /** + * 变量code + */ + private String code; + + /** + * 变量名称 + */ + private String name; + + /** + * 顺序 + */ + private Integer ordinal; +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDocContent.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDocContent.java new file mode 100644 index 000000000..5afe0359a --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxDocContent.java @@ -0,0 +1,40 @@ +package cn.axzo.workflow.core.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 文件内容 + * + * @author wangli + * @since 2025-03-27 14:09 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "ext_ax_doc_content", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ExtAxDocContent extends BaseEntity { + + private static final long serialVersionUID = 5844913474486640969L; + + /** + * 文档ID + */ + private Long fileId; + + /** + * 文档类型 + */ + private String fileType; + + /** + * 文档内容 + */ + @TableField(insertStrategy = FieldStrategy.NOT_NULL, updateStrategy = FieldStrategy.NOT_NULL) + private String content; +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxModelDoc.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxModelDoc.java new file mode 100644 index 000000000..fe00fa531 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxModelDoc.java @@ -0,0 +1,93 @@ +package cn.axzo.workflow.core.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 模板关联的文档 + * + * @author wangli + * @since 2025-03-27 14:09 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "ext_ax_model_doc", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ExtAxModelDoc extends BaseEntity { + + /** + * 文档被关联的模型 Id + */ + private String modelId; + + /** + * 文档被关联的模板对应的业务标识 + */ + private String modelKey; + + /** + * 文档关联 ID + *

+ * word/excel 对应 wps 的 Id, + * hp 对应 docContent 的 ID, + * pdf 对应 oss 中的 fileKey + */ + @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) + private String fileRelationId; + + /** + * 文档名称 + */ + private String fileName; + + /** + * 模板名称 + */ + private String templateName; + + /** + * 自动归档路径 + */ + private String location; + + /** + * 文档类型 + */ + private String fileType; + + /** + * 业务标签 + */ + private String tag; + + /** + * 启用状态 + */ + private Boolean status; + + /** + * 临时文档 + */ + private Boolean tempFile; + /** + * 是否必选 + */ + @TableField(value = "`require`") + private Boolean require; + + /** + * 排序号 + */ + @TableField(value = "`order`") + private Integer order; + + /** + * 租户 ID + */ + private String tenantId; +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessLog.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessLog.java index 7dbbdc75d..11c1db248 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessLog.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessLog.java @@ -30,6 +30,8 @@ import java.util.List; @ToString(callSuper = true) public class ExtAxProcessLog extends BaseEntity { + private static final long serialVersionUID = 461756492937079852L; + /** * 流程实例 ID */ @@ -113,6 +115,11 @@ public class ExtAxProcessLog extends BaseEntity { * 任务状态:审批中/通过/驳回/转交/加签/回退 */ private String status; + + /** + * 电子签名状态, true: 为开启 + */ + private Boolean signature; /** * 扩展字段 */ diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessSign.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessSign.java new file mode 100644 index 000000000..2f7af6885 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProcessSign.java @@ -0,0 +1,50 @@ +package cn.axzo.workflow.core.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import cn.axzo.workflow.core.conf.handler.ListSignFileDTOHandler; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +/** + * 审批日志 + * + * @author wangli + * @since 2024-08-30 15:29 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "ext_ax_process_sign", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ExtAxProcessSign extends BaseEntity { + + private static final long serialVersionUID = 461756492937079852L; + + /** + * 流程实例 ID + */ + private String processInstanceId; + /** + * 签署配置个签,还是群签 + */ + private String signType; + /** + * 签署业务待办模板 Code + */ + private String pendingMessageId; + /** + * 流程创建时,基于模型关联的模板 + */ + @TableField(typeHandler = ListSignFileDTOHandler.class) + private List docTemplate; + /** + * 文件归档 + */ + @TableField(typeHandler = ListSignFileDTOHandler.class) + private List fileArchive; +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxReModel.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxReModel.java index f1f655e8f..64541efdc 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxReModel.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxReModel.java @@ -1,6 +1,7 @@ package cn.axzo.workflow.core.repository.entity; import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import com.baomidou.mybatisplus.annotation.FieldStrategy; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @@ -35,4 +36,27 @@ public class ExtAxReModel extends BaseEntity { @TableField("status") private Integer status; + /** + * 打印开关状态(1正常 0停用) + */ + @TableField("print_status") + private Integer printStatus; + + /** + * 打印文件名称 + */ + @TableField(value = "print_file_name", updateStrategy = FieldStrategy.IGNORED) + private String printFileName; + + /** + * 打印模板名称 + */ + @TableField(value = "print_template_name", updateStrategy = FieldStrategy.IGNORED) + private String printTemplateName; + + /** + * 打印模板配置内容 + */ + @TableField(value = "print_template_config", updateStrategy = FieldStrategy.IGNORED) + private String printTemplateConfig; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxReadRecord.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxReadRecord.java new file mode 100644 index 000000000..98f66cbd9 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxReadRecord.java @@ -0,0 +1,43 @@ +package cn.axzo.workflow.core.repository.entity; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import cn.axzo.workflow.core.conf.handler.ListSimpleDocDTOHandler; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +/** + * 审批日志 + * + * @author wangli + * @since 2024-08-30 15:29 + */ +@EqualsAndHashCode(callSuper = true) +@TableName(value = "ext_ax_read_record", autoResultMap = true) +@Data +@ToString(callSuper = true) +public class ExtAxReadRecord extends BaseEntity { + + private static final long serialVersionUID = 461756492937079852L; + + /** + * 流程实例 ID + */ + private String processInstanceId; + + /** + * 自热人 ID + */ + private Long personId; + + /** + * 多文档的阅读状态记录 + */ + @TableField(typeHandler = ListSimpleDocDTOHandler.class) + private List readStatus; +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDictGroupMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDictGroupMapper.java new file mode 100644 index 000000000..0737bd894 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDictGroupMapper.java @@ -0,0 +1,9 @@ +package cn.axzo.workflow.core.repository.mapper; + +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroup; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ExtAxDictGroupMapper extends BaseMapper { +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDictGroupVariableMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDictGroupVariableMapper.java new file mode 100644 index 000000000..81d7626f7 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDictGroupVariableMapper.java @@ -0,0 +1,9 @@ +package cn.axzo.workflow.core.repository.mapper; + +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroupVariable; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ExtAxDictGroupVariableMapper extends BaseMapper { +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDocContentMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDocContentMapper.java new file mode 100644 index 000000000..e46125e42 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxDocContentMapper.java @@ -0,0 +1,8 @@ +package cn.axzo.workflow.core.repository.mapper; + +import cn.axzo.workflow.core.repository.entity.ExtAxDocContent; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ExtAxDocContentMapper extends BaseMapperX { +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxModelDocMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxModelDocMapper.java new file mode 100644 index 000000000..a232ef29e --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxModelDocMapper.java @@ -0,0 +1,12 @@ +package cn.axzo.workflow.core.repository.mapper; + +import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +@Mapper +public interface ExtAxModelDocMapper extends BaseMapperX { + + @Select("SELECT * FROM EXT_AX_MODEL_DOC WHERE ID = #{id}") + ExtAxModelDoc getIgnoreDelete(Long id); +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessLogMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessLogMapper.java index f9b3678c0..185b648ac 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessLogMapper.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessLogMapper.java @@ -2,6 +2,7 @@ package cn.axzo.workflow.core.repository.mapper; import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; @Mapper @@ -10,4 +11,7 @@ public interface ExtAxProcessLogMapper extends BaseMapperX { @Update("UPDATE ext_ax_process_log SET is_delete = 0, start_time = NOW() WHERE process_instance_id = #{processInstanceId} and task_id = #{taskId}") void restore(String processInstanceId, String taskId); + + @Select("select * from ext_ax_process_log WHERE process_instance_id = #{processInstanceId} and task_id = #{taskId}") + ExtAxProcessLog findByProcessIdAndTaskIdWithDeleted(String processInstanceId, String taskId); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessSignMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessSignMapper.java new file mode 100644 index 000000000..b5e09882c --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxProcessSignMapper.java @@ -0,0 +1,9 @@ +package cn.axzo.workflow.core.repository.mapper; + +import cn.axzo.workflow.core.repository.entity.ExtAxProcessSign; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ExtAxProcessSignMapper extends BaseMapperX { + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxReadRecordMapper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxReadRecordMapper.java new file mode 100644 index 000000000..c9819df54 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/mapper/ExtAxReadRecordMapper.java @@ -0,0 +1,9 @@ +package cn.axzo.workflow.core.repository.mapper; + +import cn.axzo.workflow.core.repository.entity.ExtAxReadRecord; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ExtAxReadRecordMapper extends BaseMapperX { + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java index f47c4bd2e..eaad658f2 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessActivityService.java @@ -4,7 +4,6 @@ import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCa import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO; -import org.flowable.job.service.impl.persistence.entity.JobEntity; /** * 流程活动的 Service diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java index 8d72ad7ff..5121fc0de 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessInstanceService.java @@ -1,16 +1,19 @@ package cn.axzo.workflow.core.service; +import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO; 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.BpmnProcessInstanceLogQueryDTO; 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.process.HistoricProcessInstanceSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; 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; @@ -18,7 +21,10 @@ import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLo 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.HistoricProcessInstanceVO; +import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO; import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; +import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO; +import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo; import com.fasterxml.jackson.databind.node.ObjectNode; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.runtime.ProcessInstance; @@ -30,6 +36,8 @@ import java.util.List; public interface BpmnProcessInstanceService { + List nodesBeforeCreateProcessInstance(BeforeProcessInstanceCreateDTO dto); + /** * 发起审核 */ @@ -42,7 +50,7 @@ public interface BpmnProcessInstanceService { * * @return true or false, true mean's cancelled */ - Boolean cancelProcessInstance(BpmnProcessInstanceCancelDTO processInstanceCancelDTO); + Boolean cancelProcessInstance(SuperBpmnProcessInstanceCancelDTO processInstanceCancelDTO); /** * 中止流程实例 @@ -165,4 +173,32 @@ public interface BpmnProcessInstanceService { * @return */ BpmnProcessInstanceLogVO getProcessInstanceLog(BpmnProcessInstanceLogQueryDTO dto); + + /** + * 根据流程实例id,任务id,查询任务状态,按钮配置 + * + * @param taskButtonsSearchDTO 请求参数 + * @return 任务按钮,状态信息 + */ + BpmnTaskButtonVo findTaskButtons(BpmnTaskButtonSearchDTO taskButtonsSearchDTO); + + /** + * 获取指定流程实例对应的模型 ID + * @param processInstanceId + * @return + */ + String getModelIdByProcessInstanceId(String processInstanceId); + + String getModelIdByProcessDefinitionId(String processInstanceId, String processDefinitionId); + + /** + * 覆盖指定流程表单的指定表单项 + * + * @param dto + */ + void overrideFormVariables(FormVariablesUpdateDTO dto); + + boolean hasPrintTemplate(String processInstanceId, String processDefinitionId); + + List processInstanceSelectDocs(ProcessDocQueryDTO dto); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessModelService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessModelService.java index aec21350e..82ab67b80 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessModelService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessModelService.java @@ -3,9 +3,11 @@ package cn.axzo.workflow.core.service; 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; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelDetailVO; +import cn.axzo.workflow.common.model.response.print.PrintModelDTO; import io.swagger.annotations.ApiOperation; import javax.annotation.Nullable; @@ -65,7 +67,13 @@ public interface BpmnProcessModelService { void changeStatus(String modelId, Integer status, BpmnTaskDelegateAssigner operator); + void changePrintStatus(String modelId, Integer status, BpmnTaskDelegateAssigner assignee); + List getModelCategoryList(); List getTenantIds(); + + void printTemplateConfig(PrintTemplateConfigUpsertDTO dto); + + PrintModelDTO getPrintTemplateConfig(String modelId); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java index 9db2a27fd..94cbfdc4a 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/BpmnProcessTaskService.java @@ -1,24 +1,9 @@ package cn.axzo.workflow.core.service; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO; -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.BpmnTaskAuditWithFormDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO; -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.request.bpmn.task.*; 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.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.axzo.workflow.common.model.response.bpmn.task.*; import org.flowable.form.api.FormInfo; import java.util.List; @@ -44,11 +29,18 @@ public interface BpmnProcessTaskService { void approveTask(BpmnTaskAuditDTO taskAuditDTO); void approveTaskWithForm(BpmnTaskAuditWithFormDTO taskAuditDto); + /** * 回退 */ void backTask(BpmnTaskBackAuditDTO taskAuditDTO); + /** + * 用于系统内部回退操作 + * @param taskAuditDTO 请求参数 + */ + void systemBackTask(BpmnNodeBackSystemOperateDTO taskAuditDTO); + /** * 批量同意 * @@ -58,9 +50,16 @@ public interface BpmnProcessTaskService { /** * 回退到指定节点,可以回退节点选项 - * @param taskId 任务id + * @param taskId 流程任务id */ - List getBackOptionalNodes(String taskId); + List getBackOptionalNodesByTaskId(String taskId); + + /** + * 回退到指定节点,可以回退节点选项 + * @param processInstanceId 流程实例id + * @param currentActivityId 当前节点定义id + */ + List getBackOptionalNodes(String processInstanceId, String currentActivityId); /** * 驳回 @@ -151,4 +150,5 @@ public interface BpmnProcessTaskService { String findTaskIdByInstanceIdAndPersonId(String processInstanceId, String personId); Map findTaskIdByInstanceIdsAndPersonId(List processInstanceIds, String personId); + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryConfigService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryConfigService.java index eb0063a1a..54f3d62f1 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryConfigService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryConfigService.java @@ -2,8 +2,11 @@ package cn.axzo.workflow.core.service; 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.CategoryGroupVarSearchDto; +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarUpsertDto; 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.CategoryGroupVarItemVo; import java.util.List; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryGroupService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryGroupService.java new file mode 100644 index 000000000..08c63adf4 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryGroupService.java @@ -0,0 +1,24 @@ +package cn.axzo.workflow.core.service; + +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarSearchDto; +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarUpsertDto; +import cn.axzo.workflow.common.model.response.category.CategoryGroupVarItemVo; + +import java.util.List; + +public interface CategoryGroupService { + + /** + * 查询业务分类下的分组以及变量 + * @param dto 请求参数 + * @return 分组以及对应变量列表 + */ + List searchGroupAndVarList(CategoryGroupVarSearchDto dto); + + /** + * 新增/更新 分组以及变量 + * @param dto 请求桉树 + * @return 是否成功 + */ + Boolean upsertGroupAndVars(CategoryGroupVarUpsertDto dto); +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryGroupVariableService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryGroupVariableService.java new file mode 100644 index 000000000..ea9e800c5 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/CategoryGroupVariableService.java @@ -0,0 +1,8 @@ +package cn.axzo.workflow.core.service; + +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroupVariable; +import com.baomidou.mybatisplus.extension.service.IService; + +public interface CategoryGroupVariableService extends IService { + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxBpmnFormRelationService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxBpmnFormRelationService.java index c39cd1488..b9277dd9a 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxBpmnFormRelationService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxBpmnFormRelationService.java @@ -4,7 +4,6 @@ import cn.axzo.workflow.common.model.dto.BpmnFormRelationCreateDTO; import cn.axzo.workflow.common.model.dto.BpmnFormRelationSearchDTO; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.core.repository.entity.ExtAxBpmnFormRelation; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import java.util.List; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxDocContentService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxDocContentService.java new file mode 100644 index 000000000..f3375e35f --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxDocContentService.java @@ -0,0 +1,28 @@ +package cn.axzo.workflow.core.service; + +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.core.repository.entity.ExtAxDocContent; + +import java.util.List; + +/** + * 文件内容表 + * + * @author wangli + * @since 2025-03-28 18:12 + */ +public interface ExtAxDocContentService { + + List getByIds(List ids); + + ExtAxDocContent createContent(String content, FileTypeEnum fileType, Long docId); + + ExtAxDocContent updateContent(String content, FileTypeEnum fileType, String docId); + + ExtAxDocContent deleteContent(String docId); + + List batchDeleteContent(List docIds); + + String getContent(Long docId); + +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxModelDocService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxModelDocService.java new file mode 100644 index 000000000..78d7dde32 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxModelDocService.java @@ -0,0 +1,128 @@ +package cn.axzo.workflow.core.service; + +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCloneDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCreateDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocOrderDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocUpdateDTO; +import cn.axzo.workflow.common.model.response.BpmPageResult; +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc; + +import java.util.List; + +/** + * 模型关联文档 + * + * @author wangli + * @since 2025-03-28 18:12 + */ +public interface ExtAxModelDocService { + + /** + * 模型文档分页搜索 + * + * @param dto + * @return + */ + BpmPageResult docPage(DocSearchDTO dto); + + List docList(DocQueryDTO dto); + + List querySetting(String key, String tenantId); + + /** + * 获取指定文档 + * + * @param docId + * @return + */ + DocBaseVO get(Long docId); + + /** + * 获取指定文档忽略删除 + * + * @param docId + * @return + */ + DocBaseVO get(Long docId, boolean ignoreDelete); + + /** + * 模型文档创建 + * + * @param dto + * @return + */ + Long createDoc(DocCreateDTO dto, Boolean fireEvent); + + /** + * 模型文档更新 + * + * @param dto + * @return + */ + Boolean updateDoc(DocUpdateDTO dto); + + /** + * 克隆文档 + * + * @param dto + * @return + */ + Long cloneDoc(DocCloneDTO dto); + + /** + * 特殊场景的克隆,一般情况下请调用上面的接口。 + *

+ * 该接口内部,会在 ext_ax_model_doc 中生成一条新的记录,但其 temp_file 标识是 true + * + * @param dto + * @param tempFile + * @return + */ + Long cloneDoc(DocCloneDTO dto, Boolean tempFile); + + /** + * 模型文档删除 + * + * @param docId + * @return + */ + Boolean deleteDoc(Long docId); + + /** + * 批量删除 + * + * @param docIds 文档列表 + * @return 是否成功 + */ + Boolean batchDeleteDoc(List docIds); + + /** + * 模型文档排序 + * + * @param dto + * @return + */ + Boolean orderDoc(DocOrderDTO dto); + + /** + * 修改文档状态 + * + * @param dto + * @return + */ + Boolean statusDoc(DocStatusDTO dto); + + /** + * 修改文档必选 + * + * @param dto + * @return + */ + Boolean requireDoc(DocStatusDTO dto); + + List getIds(List ids); +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessLogService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessLogService.java index 238a57e8c..1271f1ed8 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessLogService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessLogService.java @@ -55,8 +55,11 @@ public interface ExtAxProcessLogService { List genericQuery(ExtAxProcessLog query); + ExtAxProcessLog findByProcessIdAndTaskIdWithDeleted(String processId, String taskId); + /** * 机器人节点会删除日志,这里仅仅为了恢复日志都删除标识,并更新时间。 + * * @param processInstanceId * @param taskId */ diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessSignService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessSignService.java new file mode 100644 index 000000000..ec221457d --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxProcessSignService.java @@ -0,0 +1,15 @@ +package cn.axzo.workflow.core.service; + +import cn.axzo.workflow.core.repository.entity.ExtAxProcessSign; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * Api Log 表操作服务 + * + * @author wangli + * @since 2024/4/3 10:40 + */ +public interface ExtAxProcessSignService extends IService { + + ExtAxProcessSign findByProcessInstanceId(String processInstanceId); +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxReModelService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxReModelService.java index 9c3fa39de..5dc3301f2 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxReModelService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxReModelService.java @@ -1,6 +1,9 @@ package cn.axzo.workflow.core.service; +import cn.axzo.workflow.common.enums.ExtModelStateFieldEnum; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.model.response.print.PrintModelDTO; import cn.axzo.workflow.core.repository.entity.ExtAxReModel; import java.util.List; @@ -18,7 +21,26 @@ public interface ExtAxReModelService { void update(String modelId, Integer status); - void changeStatus(String modelId, Integer status); + /** + * 修改模型扩展表的状态 + * + * @param modelId + * @param status + * @param field “status”代运营开关状态 or “printStatus”打印开关状态 + */ + void changeStatus(String modelId, Integer status, ExtModelStateFieldEnum field); BpmnModelExtVO getStatusByModelId(String modelId); + + void printTemplateConfig(PrintTemplateConfigUpsertDTO dto); + + /** + * 本方法主要判断开关以及有打印模板 + * + * @param modelId + * @return + */ + Boolean hasPrintTemplateConfig(String modelId); + + PrintModelDTO getPrintTemplateConfig(String modelId); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxReadRecordService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxReadRecordService.java new file mode 100644 index 000000000..cbc092036 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxReadRecordService.java @@ -0,0 +1,22 @@ +package cn.axzo.workflow.core.service; + +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO; +import cn.axzo.workflow.core.repository.entity.ExtAxReadRecord; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.util.List; + +/** + * 审批人关联文档阅读记录表操作服务 + * + * @author wangli + * @since 2024/4/3 10:40 + */ +public interface ExtAxReadRecordService extends IService { + + List queryReadStatus(ApproverReadStatusDTO dto); + + Boolean changeReadStatus(ChangeApproverReadStatusDTO dto); +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/FormCoreService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/FormCoreService.java index 8f4d56331..2729776e5 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/FormCoreService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/FormCoreService.java @@ -1,10 +1,8 @@ package cn.axzo.workflow.core.service; import cn.axzo.workflow.common.model.dto.BpmnFormRelationSearchDTO; -import cn.axzo.workflow.common.model.request.form.definition.FormDefinitionSearchDTO; import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO; import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO; -import cn.axzo.workflow.common.model.request.form.instance.FormInstanceSearchDTO; import cn.axzo.workflow.common.model.response.form.FormVO; import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO; import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/CategoryConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/CategoryConverter.java index 41dceacae..579e56ac4 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/CategoryConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/converter/CategoryConverter.java @@ -7,7 +7,7 @@ import org.mapstruct.Mapping; import java.util.Arrays; -import static org.mapstruct.NullValueCheckStrategy.ALWAYS; +import static org.mapstruct.NullValueCheckStrategy.*; /** * 分类的 MapStruts 转换器 @@ -27,6 +27,9 @@ public interface CategoryConverter extends EntityConverter optCategory = categoryService.get(BPM_MODEL_CATEGORY, dto.getKey()); + BpmnModel bpmnModel = BpmnJsonConverterUtil.convertToBpmn(dto.getJsonModel().getNode(), + ModelBizTypeEnum.valueOfType(optCategory.get().getValue()), StringUtils.hasLength(dto.getKey()) ? dto.getKey() : dto.getCategory(), dto.getName(), dto.getFormKey(), dto.getDescription(), dto.getJsonModel().getApproveConf(), + dto.getJsonModel().getSignConf(), dto.getJsonModel().getNoticeConf(), dto.getJsonModel().getButtonConf(), dto.getJsonModel().getFieldConf(), @@ -196,7 +219,7 @@ public class BpmnProcessDefinitionServiceImpl implements BpmnProcessDefinitionSe vo.setJsonModel(BpmnJsonConverterUtil.convertToJson(repositoryService.getBpmnModel(id))); ExtAxBpmnFormRelation relation = bpmnFormRelationService.queryByBpmnDefinitionId(processDefinition.getId()); - if(Objects.isNull(relation)){ + if (Objects.isNull(relation)) { return vo; } FormDefinitionSearchDTO formDefinitionSearch = new FormDefinitionSearchDTO(); @@ -252,6 +275,15 @@ public class BpmnProcessDefinitionServiceImpl implements BpmnProcessDefinitionSe processDefinition = filterDefinitions.stream().filter(i -> Objects.equals(tenantId, i.getTenantId())).findFirst() .orElseThrow(() -> new WorkflowEngineException(PROCESS_DEFINITION_KEY_NOT_EXISTS, key)); + // 判断代运营是否是关闭状态 + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + Model model = commandExecutor.execute(new CustomGetModelByDefinitionIdCmd(processDefinition.getId())); + BpmnModelExtVO modelStatus = extAxReModelService.getStatusByModelId(model.getId()); + if (Objects.equals(0, modelStatus.getStatus())) { + processDefinition = + filterDefinitions.stream().filter(i -> Objects.equals(NO_TENANT_ID, i.getTenantId())).findFirst() + .orElseThrow(() -> new WorkflowEngineException(PROCESS_DEFINITION_KEY_NOT_EXISTS, key)); + } } return processDefinitionConverter.toVo(processDefinition); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceForEsServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceForEsServiceImpl.java index aaa2a838c..14e991b64 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceForEsServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceForEsServiceImpl.java @@ -11,7 +11,6 @@ import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstanceQuery; -import org.flowable.engine.impl.HistoryServiceImpl; import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.variable.api.history.HistoricVariableInstance; import org.springframework.context.annotation.Lazy; diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java index d768c744f..040de2efe 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessInstanceServiceImpl.java @@ -1,20 +1,27 @@ package cn.axzo.workflow.core.service.impl; +import cn.axzo.basics.common.BeanMapper; +import cn.axzo.basics.common.exception.ServiceException; import cn.axzo.workflow.common.constant.BpmnConstants; import cn.axzo.workflow.common.enums.ApprovalMethodEnum; +import cn.axzo.workflow.common.enums.ApproverSpecifyEnum; import cn.axzo.workflow.common.enums.AttachmentTypeEnum; import cn.axzo.workflow.common.enums.BpmnFlowNodeMode; import cn.axzo.workflow.common.enums.BpmnFlowNodeType; import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; +import cn.axzo.workflow.common.enums.BpmnProcessTaskResultEnum; +import cn.axzo.workflow.common.enums.BusinessTypeEnum; +import cn.axzo.workflow.common.enums.ButtonVisibleScopeEnum; import cn.axzo.workflow.common.enums.WorkspaceType; import cn.axzo.workflow.common.exception.WorkflowEngineException; -import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; import cn.axzo.workflow.common.model.request.BpmnApproveConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo; +import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO; 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; @@ -22,37 +29,57 @@ import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceLog 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.process.HistoricProcessInstanceSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO; import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO; import cn.axzo.workflow.common.model.request.category.CategorySearchDTO; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.BatchOperationItemResultVO; import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO; +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import cn.axzo.workflow.common.model.response.bpmn.process.BpmnNodeConfigVO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO; 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.HistoricProcessInstanceVO; +import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO; import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; +import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO; +import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceLogVO; import cn.axzo.workflow.common.model.response.category.CategoryItemVO; import cn.axzo.workflow.core.common.utils.BpmnCollectionUtils; +import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceAsyncCmd; import cn.axzo.workflow.core.engine.cmd.CustomAbortProcessInstanceCmd; import cn.axzo.workflow.core.engine.cmd.CustomCancelProcessInstanceAsyncCmd; import cn.axzo.workflow.core.engine.cmd.CustomCancelProcessInstanceCmd; import cn.axzo.workflow.core.engine.cmd.CustomCarbonCopyUserSelectorCmd; import cn.axzo.workflow.core.engine.cmd.CustomForecastUserTaskAssigneeCmd; +import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd; +import cn.axzo.workflow.core.engine.cmd.CustomOverrideFormVariablesByLatestInstanceCmd; import cn.axzo.workflow.core.engine.listener.EngineExecutionStartListener; import cn.axzo.workflow.core.repository.entity.ExtAxBpmnFormRelation; +import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst; import cn.axzo.workflow.core.repository.entity.ExtAxProcessLog; +import cn.axzo.workflow.core.repository.entity.ExtAxProcessSign; +import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper; import cn.axzo.workflow.core.service.BpmnProcessDefinitionService; import cn.axzo.workflow.core.service.BpmnProcessInstanceService; import cn.axzo.workflow.core.service.CategoryService; import cn.axzo.workflow.core.service.ExtAxBpmnFormRelationService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxProcessLogService; +import cn.axzo.workflow.core.service.ExtAxProcessSignService; +import cn.axzo.workflow.core.service.ExtAxReModelService; +import cn.axzo.workflow.core.service.ExtAxReadRecordService; import cn.axzo.workflow.core.service.converter.BpmnHistoricProcessInstanceConverter; import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter; import cn.axzo.workflow.core.service.converter.BpmnProcessInstanceAdminPageItemConverter; @@ -60,8 +87,9 @@ import cn.axzo.workflow.core.service.converter.BpmnProcessInstanceConverter; import cn.axzo.workflow.core.service.converter.BpmnProcessInstancePageItemConverter; import cn.axzo.workflow.core.service.support.FlowNodeForecastService; import cn.axzo.workflow.core.service.support.ProcessGraphicService; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.json.JSONUtil; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Lists; @@ -70,6 +98,7 @@ import org.apache.commons.collections4.ListUtils; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.ReceiveTask; +import org.flowable.bpmn.model.SequenceFlow; import org.flowable.bpmn.model.ServiceTask; import org.flowable.bpmn.model.UserTask; import org.flowable.common.engine.impl.db.SuspensionState; @@ -111,7 +140,6 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -128,29 +156,42 @@ import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_S import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS; import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.RUNNING_INSTANCE_ONLY_FORECAST; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_NOT_EXISTS; import static cn.axzo.workflow.common.code.BpmnProcessDefinitionRespCode.PROCESS_DEFINITION_ID_NOT_EXISTS; import static cn.axzo.workflow.common.code.BpmnProcessDefinitionRespCode.PROCESS_DEFINITION_IS_INVALID; import static cn.axzo.workflow.common.code.BpmnProcessDefinitionRespCode.PROCESS_DEFINITION_IS_SUSPENDED; import static cn.axzo.workflow.common.code.BpmnProcessDefinitionRespCode.PROCESS_DEFINITION_KEY_NOT_EXISTS; import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION; import static cn.axzo.workflow.common.constant.BpmnConstants.BIZ_ORG_RELATION; +import static cn.axzo.workflow.common.constant.BpmnConstants.BPMN_FILE_SUFFIX; import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; +import static cn.axzo.workflow.common.constant.BpmnConstants.CANCEL_PROCESS_NODE_DEF_KEY_NAME; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_CARBON_COPY; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_CURRENT; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_HISTORY; import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_TYPE_INITIATOR; import static cn.axzo.workflow.common.constant.BpmnConstants.CREATE_INSTANCE_PARAMS; import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121; +import static cn.axzo.workflow.common.constant.BpmnConstants.INITIATOR_SPECIFY; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_AGENT; +import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_BIZ_TYPE; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_WORKSPACE_TYPE; +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_INITIATOR; import static cn.axzo.workflow.common.constant.BpmnConstants.PENDING_TEMPLATE_VARIABLE; import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_OWNERSHIP_APPLICATION; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGNATORIES; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_PROCESS_ENABLE_DOC_IDS; +import static cn.axzo.workflow.common.constant.BpmnConstants.SIGN_VARIABLE; import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION; +import static cn.axzo.workflow.common.constant.MetaInfoConstants.MODEL_TYPE_PROCESS; import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.autoPassed; +import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.autoPassed_empty; import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.autoRejection; +import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.autoRejection_empty; import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.human; +import static cn.axzo.workflow.common.enums.ApprovalMethodEnum.transferToAdmin; import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.AND; import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.EXCEPTIONAL; import static cn.axzo.workflow.common.enums.BpmnFlowNodeMode.GENERAL; @@ -159,21 +200,28 @@ import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CARBON_COPY; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_COMMENT; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_ROBOT; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_SIGN; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.CANCELLED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED; import static cn.axzo.workflow.common.enums.WorkspaceType.GOVERNMENT; import static cn.axzo.workflow.common.util.BpmnNativeQueryUtil.countSql; import static cn.axzo.workflow.common.util.BpmnNativeQueryUtil.sqlConnectors; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverEmptyHandleType; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverScope; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getButtonConfig; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getCarbonCopyConfigs; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNodeType; import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getProcessApproveConf; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getSignApproverLimit; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getSupportInitiatorSpecified; @Service @Slf4j @@ -223,7 +271,13 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic @Resource private ExtAxBpmnFormRelationService bpmnFormRelationService; @Resource - private ObjectMapper objectMapper; + private ExtAxReModelService extAxReModelService; + @Resource + private ExtAxModelDocMapper extAxModelDocMapper; + @Resource + private ExtAxProcessSignService extAxProcessSignService; + @Resource + private ExtAxReadRecordService extAxReadRecordService; @Override public HistoricProcessInstance getProcessInstanceByBusinessKey(String businessKey, @Nullable String tenantId, @@ -330,7 +384,63 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic return false; } + @Override + public List nodesBeforeCreateProcessInstance(BeforeProcessInstanceCreateDTO dto) { + BpmnProcessDefinitionVO definition = processDefinitionService.getActiveProcessDefinitionByKey(dto.getProcessDefinitionKey(), dto.getTenantId()); + if (Objects.isNull(definition)) { + throw new WorkflowEngineException(PROCESS_DEFINITION_KEY_NOT_EXISTS, dto.getProcessDefinitionKey()); + } + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), definition.getSuspensionState())) { + throw new WorkflowEngineException(PROCESS_DEFINITION_IS_SUSPENDED, dto.getProcessDefinitionKey()); + } + BpmnModel bpmnModel = repositoryService.getBpmnModel(definition.getId()); + List orderedNodes = new ArrayList<>(); + forecastService.doForecasting(null, null, null, orderedNodes, bpmnModel); + List nodes = new ArrayList<>(); + orderedNodes.forEach(fe -> { + if (!(fe instanceof SequenceFlow) && !Objects.equals(fe.getId(), "startEventNode") && !Objects.equals(fe.getId(), "endEventNode")) { + BpmnFlowNodeMode nodeModel = GENERAL; + if (fe instanceof UserTask && ((UserTask) fe).getBehavior() instanceof MultiInstanceActivityBehavior) { + MultiInstanceActivityBehavior behavior = (MultiInstanceActivityBehavior) ((UserTask) fe).getBehavior(); + nodeModel = (Objects.equals(AND_SIGN_EXPRESSION, + behavior.getCompletionCondition()) ? AND : OR); + } + ApproverSpecifyEnum approverSpecifyEnum = getApproverSpecify(fe).orElse(null); + nodes.add(new NodesByModelVO() + .setActivityId(fe.getId()) + .setActivityName(fe.getName()) + .setAssigners(Objects.equals(NODE_STARTER.getType(), fe.getId()) ? Lists.newArrayList(dto.getInitiator()) : calcSpecifyAssigners(approverSpecifyEnum, fe)) + .setSupportSpecify(getSupportInitiatorSpecified(fe)) + .setNodeModel(nodeModel) + .setNodeType(getNodeType(fe).orElse(null)) + .setNodeConfig(BpmnNodeConfigVO.builder() + .approvalMethodEnum(getApprovalMethod(fe).orElse(null)) + .approverScopeEnum(getApproverScope(fe).orElse(null)) + .approverSpecifyEnum(approverSpecifyEnum) + .approverEmptyHandleTypeEnum(getApproverEmptyHandleType(fe).orElse(null)) + .signature(getActivitySignature(fe)) + .signApproverLimit(getSignApproverLimit(fe).orElse(null)) + .build()) + ); + } + }); + if (CollectionUtils.isEmpty(dto.getFilter())) { + return nodes; + } + return nodes.stream().filter(i -> dto.getFilter().contains(i.getNodeType())).collect(Collectors.toList()); + } + + private List calcSpecifyAssigners(ApproverSpecifyEnum approverSpecifyEnum, FlowElement fe) { + List assigners = new ArrayList<>(); + if (Objects.nonNull(approverSpecifyEnum) && Objects.equals(approverSpecifyEnum, ApproverSpecifyEnum.fixedPerson)) { + BpmnMetaParserHelper.getApproverSpecifyValue(fe).ifPresent(s -> + assigners.addAll(JSON.parseArray(s, BpmnTaskDelegateAssigner.class))); + } + return assigners; + } + + // TODO 该接口有个问题,如果模型没有关联表单,但是传入参数中含有表单变量,则不能正确创建流程 @Override public String createProcessInstance(BpmnProcessInstanceCreateDTO dto) { BpmnProcessDefinitionVO definition = null; @@ -354,7 +464,16 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic } } - categoryItemVO.ifPresent(itemVO -> dto.getVariables().put(INTERNAL_PROCESS_WORKSPACE_TYPE, WorkspaceType.getType(Integer.valueOf(itemVO.getWorkspaceTypeCode())).getCode())); + categoryItemVO.ifPresent(itemVO -> { + dto.getVariables().put(INTERNAL_PROCESS_WORKSPACE_TYPE, WorkspaceType.getType(Integer.valueOf(itemVO.getWorkspaceTypeCode())).getCode()); + dto.getVariables().put(INTERNAL_PROCESS_BIZ_TYPE, itemVO.getBusinessType().getType()); + if (Objects.equals(BusinessTypeEnum.SIGN, itemVO.getBusinessType())) { + // 签署业务 + dto.getVariables().put(SIGN_PROCESS_ENABLE_DOC_IDS, dto.getDocIds()); + dto.getVariables().put(SIGN_VARIABLE, dto.getBizCustomVariables()); + dto.getVariables().put(SIGNATORIES, dto.getSignatories()); + } + }); dto.getVariables().put(INTERNAL_INITIATOR, dto.getInitiator().toJson()); dto.getVariables().put(BIZ_ORG_RELATION, dto.getCooperationOrg()); dto.getVariables().put(WORKFLOW_ENGINE_VERSION, serviceVersion); @@ -363,6 +482,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); dto.getVariables().put(PROCESS_OWNERSHIP_APPLICATION, request.getHeader(HEADER_SERVER_NAME)); + // 发起人指定节点的审批人集合,此处不校验集合,如果人员为空,就走节点的审批人为空的逻辑 + dto.getVariables().put(INITIATOR_SPECIFY, dto.getSpecifyAssignerMap()); // 创建流程实例 // 设置流程实例的开始人,参考 https://wenku.baidu.com/view/5538062e7a563c1ec5da50e2524de518964bd3f9.html @@ -379,25 +500,15 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic ExtAxBpmnFormRelation relation = bpmnFormRelationService.queryByBpmnDefinitionId(definition.getId()); if (Objects.isNull(relation)) { - // 如果模型没有绑定表单,则强制情况表单相关属性,避免报错 - instanceBuilder.startFormVariables(null).outcome(null); + // 如果模型没有绑定表单,则强制清空表单相关属性,避免异常 + instanceBuilder.outcome(null); } else { - // TODO 可以使用 cn.axzo.workflow.core.service.impl.BpmnProcessDefinitionServiceImpl.getProcessDefinition 接口拉取模型后进行表单数据转型,字段验证的扩展实现 - // 关于表单目前只有两类组件,由于研发资源限制,暂不做扩展性设计 - dto.getStartFormVariables().entrySet().forEach(e -> { - if (e.getValue() instanceof Collection) { - List convertUploads = ((Collection) e.getValue()).stream().map(i -> { - if (i instanceof String) { - return UploadFieldDTO.toObject(String.valueOf(i)).toSpecString(); - } else { - return objectMapper.convertValue(i, UploadFieldDTO.class).toSpecString(); - } - }).collect(Collectors.toList()); - e.setValue(StringUtils.collectionToCommaDelimitedString(convertUploads)); - } - }); - instanceBuilder.startFormVariables(dto.getStartFormVariables()) - .outcome(dto.getOutcome()); + if (!CollectionUtils.isEmpty(dto.getStartFormVariables())) { + instanceBuilder.startFormVariables(dto.getStartFormVariables()); + } + if (StringUtils.hasText(dto.getOutcome())) { + instanceBuilder.outcome(dto.getOutcome()); + } } dto.getVariables().put(CREATE_INSTANCE_PARAMS, JSONUtil.toJsonStr(dto)); instanceBuilder.variables(dto.getVariables()); @@ -419,7 +530,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic } @Override - public Boolean cancelProcessInstance(BpmnProcessInstanceCancelDTO dto) { + public Boolean cancelProcessInstance(SuperBpmnProcessInstanceCancelDTO dto) { CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); if (dto.getAsync() != null && dto.getAsync()) { commandExecutor.execute(new CustomCancelProcessInstanceAsyncCmd(dto)); @@ -475,7 +586,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic } @Override - public BpmPageResult getAdminProcessInstancePage(BpmnProcessInstanceAdminPageReqVO dto) { + public BpmPageResult getAdminProcessInstancePage + (BpmnProcessInstanceAdminPageReqVO dto) { List dictValues = new ArrayList<>(); if (StringUtils.hasText(dto.getKey())) { CategorySearchDTO searchDTO = new CategorySearchDTO(); @@ -785,7 +897,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic } @Override - public BpmPageResult historicProcessInstancePage(HistoricProcessInstanceSearchDTO dto) { + public BpmPageResult historicProcessInstancePage(HistoricProcessInstanceSearchDTO + dto) { NativeHistoricProcessInstanceQuery query = historyService.createNativeHistoricProcessInstanceQuery(); String tableName = managementService.getTableName(HistoricProcessInstance.class); StringBuilder baseQuerySql = new StringBuilder("SELECT a.* FROM ").append(tableName).append(" a"); @@ -846,7 +959,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic NativeHistoricProcessInstanceQuery countSqlQuery = query.sql(countSql(baseQuerySql)); if (StringUtils.hasText(dto.getKey())) { - return new BpmPageResult(historicProcessInstanceConverter.toVos(instances, dto.getKey()), + return new BpmPageResult<>(historicProcessInstanceConverter.toVos(instances, dto.getKey()), countSqlQuery.count()); } else { Set procDefIds = new HashSet<>(); @@ -858,7 +971,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic List vos = new ArrayList<>(); instances.forEach(i -> vos.add(historicProcessInstanceConverter.toVo(i, defCategoryMap.getOrDefault(i.getProcessDefinitionId(), "")))); - return new BpmPageResult(vos, countSqlQuery.count()); + return new BpmPageResult<>(vos, countSqlQuery.count()); } } @@ -870,7 +983,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic } @Override - public List getProcessInstanceNodeForecastWithSpecifyTaskDefinitionKey(String processInstanceId, + public List getProcessInstanceNodeForecastWithSpecifyTaskDefinitionKey(String + processInstanceId, ProcessInstance instance, String startNodeDefinitionKey, Boolean containSelf, @@ -879,7 +993,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic instance = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId).singleResult(); if (Objects.isNull(instance)) { - if (checkAliveThrowException) { + if (Boolean.TRUE.equals(checkAliveThrowException)) { throw new WorkflowEngineException(RUNNING_INSTANCE_ONLY_FORECAST); } else { return Collections.emptyList(); @@ -894,7 +1008,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic @Override - public List getProcessInstanceNodeFilterForecast(String processInstanceId, String tenantId, List nodeDefinitionKeys) { + public List getProcessInstanceNodeFilterForecast(String processInstanceId, String + tenantId, List nodeDefinitionKeys) { ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId); if (StringUtils.hasText(tenantId)) { @@ -909,7 +1024,9 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic return buildNodeDetailVos(processInstanceId, nodeDefinitionKeys, instance, flowElements); } - private List buildNodeDetailVos(String processInstanceId, List nodeDefinitionKeys, ProcessInstance instance, List flowElements) { + private List buildNodeDetailVos(String + processInstanceId, List nodeDefinitionKeys, ProcessInstance + instance, List flowElements) { BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId()); List resultList = new ArrayList<>(flowElements.size()); // 发起人节点,也是一个 UserTask 节点, 所以这里默认就包含了发起人节点 @@ -938,7 +1055,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic return resultList; } - private void parseServiceTask(String processInstanceId, ServiceTask i, ProcessNodeDetailVO node, List skipTaskDefinitionKeys) { + private void parseServiceTask(String processInstanceId, ServiceTask i, ProcessNodeDetailVO + node, List skipTaskDefinitionKeys) { // ServiceTask 主要作用于抄送 node.setId(i.getId()).setName(i.getName()); if (skipTaskDefinitionKeys.contains(i.getId())) { @@ -951,7 +1069,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic historicTaskInstanceConverter, serviceVersion)))); } - private void parseUserTask(String processInstanceId, UserTask i, ProcessNodeDetailVO node, List skipTaskDefinitionKeys) { + private void parseUserTask(String processInstanceId, UserTask i, ProcessNodeDetailVO + node, List skipTaskDefinitionKeys) { node.setFormKey(i.getFormKey()); // 设置审批模式, if (i.getBehavior() instanceof MultiInstanceActivityBehavior) { @@ -978,10 +1097,13 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic getApproverEmptyHandleType(i).ifPresent(emptyHandleType -> { switch (emptyHandleType) { case autoPassed: - node.setApprovalMethod(autoPassed); + node.setApprovalMethod(autoPassed_empty); break; case autoRejection: - node.setApprovalMethod(autoRejection); + node.setApprovalMethod(autoRejection_empty); + break; + case transferToAdmin: + node.setApprovalMethod(transferToAdmin); break; default: node.setNodeMode(EXCEPTIONAL); @@ -1004,7 +1126,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic @Override public Boolean checkInstanceApprover(BpmnProcessInstanceCheckApproverDTO dto) { - if (dto.getOnlyPersonId()) { + if (Boolean.TRUE.equals(dto.getOnlyPersonId())) { dto.getApprover().setOuId(null); } List list = taskService.createTaskQuery() @@ -1066,19 +1188,178 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic .supportBatchOperation(getProcessApproveConf(bpmnModel.getMainProcess()).orElse(new BpmnApproveConf()).getSupportBatchOperation()) .userAgreeSignature(getProcessApproveConf(bpmnModel.getMainProcess()).orElse(new BpmnApproveConf()).getUserAgreeSignature()) .workflowEngineVersion((String) variables.getOrDefault(WORKFLOW_ENGINE_VERSION, FLOW_SERVER_VERSION_121)) + .catPrint(hasPrintTemplate(null, historicProcessInstance.getProcessDefinitionId())) .build(); - categoryService.get(BPM_MODEL_CATEGORY, historicProcessInstance.getProcessDefinitionKey()).ifPresent(category -> { - logVO.setWorkspaceType(WorkspaceType.getType(Integer.valueOf(category.getWorkspaceTypeCode()))); - logVO.setCategory(category.getValue()); - }); + categoryService.get(BPM_MODEL_CATEGORY, historicProcessInstance.getProcessDefinitionKey()) + .ifPresent(category -> { + logVO.setWorkspaceType(WorkspaceType.getType(Integer.valueOf(category.getWorkspaceTypeCode()))); + logVO.setCategory(category.getValue()); + }); // 根据传入的访问人计算有权限的按钮 calcAuthorizedButtons(logVO, dto.getVisitor()); return logVO; } + + @Override + public BpmnTaskButtonVo findTaskButtons(BpmnTaskButtonSearchDTO taskButtonsSearchDTO) { + BpmnTaskButtonVo bpmnTaskButtonVo = new BpmnTaskButtonVo(); + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(taskButtonsSearchDTO.getProcessInstanceId()) + .includeProcessVariables() + .singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new WorkflowEngineException(PROCESS_INSTANCE_ID_NOT_EXISTS, taskButtonsSearchDTO.getProcessInstanceId()); + } + BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId()); + ExtAxHiTaskInst extAxHiTaskInst = extAxHiTaskInstService.getByTaskId(taskButtonsSearchDTO.getTaskId(), taskButtonsSearchDTO.getProcessInstanceId()); + if (extAxHiTaskInst == null) { + log.warn("extAxHiTaskInst is null,request param:{}", JSON.toJSONString(taskButtonsSearchDTO)); + throw new ServiceException("can find extAxHiTaskInst"); + } + if (Objects.equals(extAxHiTaskInst.getStatus(), DELETED.getStatus())) { + ExtHiTaskSearchDTO extHiTaskSearchDTO = ExtHiTaskSearchDTO.builder() + .processInstanceId(taskButtonsSearchDTO.getProcessInstanceId()) + .taskDefinitionKey(extAxHiTaskInst.getTaskDefinitionKey()) + .assignee(extAxHiTaskInst.getAssignee()) + .excludeIds(Collections.singletonList(extAxHiTaskInst.getId())) + .build(); + List extAxHiTaskInsts = extAxHiTaskInstService.queryList(extHiTaskSearchDTO); + log.info("extAxHiTaskInst status is DELETED, find another task list in same assignee and task def key,result is:{}", JSON.toJSONString(extAxHiTaskInsts)); + if (!CollectionUtils.isEmpty(extAxHiTaskInsts)) { + extAxHiTaskInst = extAxHiTaskInsts.get(0); + } + } + //判断节点类型 + FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(extAxHiTaskInst.getTaskDefinitionKey()); + BpmnFlowNodeType nodeType = getNodeType(flowElement).orElse(null); + //设置任务状态 + bpmnTaskButtonVo.setExecutorTaskResult(getTaskStatus(nodeType, extAxHiTaskInst)); + String buttonConfigName = null; + BpmnButtonConf buttonConf = getButtonConfig(bpmnModel.getMainProcess(), extAxHiTaskInst.getTaskDefinitionKey()).orElse(null); + BpmnProcessInstanceLogVO logVO = BpmnProcessInstanceLogVO.builder() + .id(historicProcessInstance.getId()) + .defaultButtonConf(getButtonConfig(bpmnModel.getMainProcess()).orElse(new BpmnButtonConf())) + .calculatingButtonConf(buttonConf) + .build(); + List carbonCopyButtons = chooseButtons(logVO, CONFIG_BUTTON_TYPE_CARBON_COPY); + List initiatorButtons; + List executorButtons; + if (Objects.equals(historicProcessInstance.getBusinessStatus(), PROCESSING.getStatus())) { + //发起人按钮 + initiatorButtons = chooseButtons(logVO, CONFIG_BUTTON_TYPE_INITIATOR); + if (Objects.equals(nodeType, BpmnFlowNodeType.NODE_CARBON_COPY)) { + buttonConfigName = CONFIG_BUTTON_TYPE_CARBON_COPY; + } else if (Objects.equals(nodeType, BpmnFlowNodeType.NODE_TASK) || //审批节点加业务节点指定审批人 + (Objects.equals(nodeType, BpmnFlowNodeType.NODE_BUSINESS) && flowElement.getClass().isAssignableFrom(UserTask.class))) { + // 待审批的节点 + if ((Objects.equals(PROCESSING.getStatus(), extAxHiTaskInst.getStatus()))) { + buttonConfigName = CONFIG_BUTTON_TYPE_CURRENT; + } + //已审批节点 + if (!Objects.equals(PROCESSING.getStatus(), extAxHiTaskInst.getStatus())) { + buttonConfigName = CONFIG_BUTTON_TYPE_HISTORY; + } + } else if (Objects.equals(nodeType, BpmnFlowNodeType.NODE_STARTER)) { + buttonConfigName = CONFIG_BUTTON_TYPE_INITIATOR; + } + if (!StringUtils.hasText(buttonConfigName)) { + log.warn("can't find button config name ,req:{},extAxHiTaskInst:{}", JSON.toJSONString(taskButtonsSearchDTO), JSON.toJSONString(extAxHiTaskInst)); + throw new ServiceException("can't find button config name"); + } + executorButtons = chooseButtons(logVO, buttonConfigName); + } else { + //需要判断流程是否撤回,撤回需要设置发起人状态 + if (Objects.equals(historicProcessInstance.getBusinessStatus(), CANCELLED.getStatus())) { + Object object = historicProcessInstance.getProcessVariables().get(CANCEL_PROCESS_NODE_DEF_KEY_NAME); + if (object instanceof String) { + String cancelNodeKey = (String) object; + if (StringUtils.hasText(cancelNodeKey) && cancelNodeKey.equals(extAxHiTaskInst.getTaskDefinitionKey())) { + bpmnTaskButtonVo.setInitiatorTaskResult(BpmnProcessTaskResultEnum.CANCELED); + } + } + } + initiatorButtons = new ArrayList<>(carbonCopyButtons); + executorButtons = new ArrayList<>(carbonCopyButtons); + } + + bpmnTaskButtonVo.setCustomHiddenButtons(getHiddenCustomButtons(logVO, executorButtons)); + //设置allConfigButtons + List allConfigButtons = new ArrayList<>(executorButtons); + bpmnTaskButtonVo.setAllConfigButtons(allConfigButtons); + Map> metaInfoListMap = new HashMap<>(); + if (!CollectionUtils.isEmpty(executorButtons)) { + for (BpmnButtonMetaInfo metaInfo : filterValuableButtons(executorButtons)) { + metaInfoListMap.computeIfAbsent(metaInfo, k -> new HashSet<>()).add(ButtonVisibleScopeEnum.EXECUTOR); + } + } + if (!CollectionUtils.isEmpty(initiatorButtons)) { + for (BpmnButtonMetaInfo metaInfo : filterValuableButtons(initiatorButtons)) { + metaInfoListMap.computeIfAbsent(metaInfo, k -> new HashSet<>()).add(ButtonVisibleScopeEnum.INITIATOR); + } + } + + List scopeButtons = metaInfoListMap.entrySet().stream() + .map(m -> { + BpmnTaskButtonVo.BpmnButtonMetaInfoWithVisibleScope scopeButton = BeanUtil.copyProperties(m.getKey(), BpmnTaskButtonVo.BpmnButtonMetaInfoWithVisibleScope.class); + scopeButton.setVisibleScopes(new ArrayList<>(m.getValue())); + return scopeButton; + }) + .collect(Collectors.toList()); + bpmnTaskButtonVo.setButtons(scopeButtons); + return bpmnTaskButtonVo; + } + + private BpmnProcessTaskResultEnum getTaskStatus(BpmnFlowNodeType nodeType, ExtAxHiTaskInst axHiTaskInst) { + String logResultStatus = axHiTaskInst.getStatus(); + if (!StringUtils.hasText(logResultStatus)) { + log.warn("log result status is blank!"); + return null; + } + BpmnProcessTaskResultEnum taskResult = null; + BpmnProcessInstanceResultEnum instanceResultEnum = BpmnProcessInstanceResultEnum.valueOf(logResultStatus); + switch (instanceResultEnum) { + case CANCELLED: + taskResult = BpmnProcessTaskResultEnum.CANCELED; + break; + case APPROVED: + if (Objects.equals(nodeType, BpmnFlowNodeType.NODE_CARBON_COPY)) { + taskResult = BpmnProcessTaskResultEnum.NONE; + } else { + taskResult = BpmnProcessTaskResultEnum.APPROVED; + } + break; + case REJECTED: + taskResult = BpmnProcessTaskResultEnum.REJECTED; + break; + case TRANSFER: + taskResult = BpmnProcessTaskResultEnum.TRANSFERRED; + break; + case PROCESSING: + taskResult = BpmnProcessTaskResultEnum.PENDING; + break; + case DELETED: + taskResult = BpmnProcessTaskResultEnum.DELETED; + break; + default: + break; + } + return taskResult; + } + + @Override + public boolean hasPrintTemplate(String processInstanceId, String processDefinitionId) { + try { + return extAxReModelService.hasPrintTemplateConfig(getModelIdByProcessDefinitionId(processInstanceId, processDefinitionId)); + } catch (Exception e) { + log.warn("hasPrintTemplate error:{}", e.getMessage(), e); + return false; + } + } + private void calcAuthorizedButtons(BpmnProcessInstanceLogVO logVO, BpmnTaskDelegateAssigner visitor) { List authorizedButtons = new ArrayList<>(); if (Objects.nonNull(logVO.getDefaultButtonConf()) @@ -1086,7 +1367,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic authorizedButtons.addAll(logVO.getDefaultButtonConf().getCarbonCopy()); } - if (Objects.equals(PROCESSING, logVO.getResult()) && Objects.nonNull(visitor)) { + if (Objects.nonNull(visitor)) { String ge130Assignee = getGe130Assignee(visitor); String le130Assignee = getLe130Assignee(visitor); @@ -1098,7 +1379,7 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic // 比对发起人 - if (Objects.nonNull(logVO.getInitiator()) && + if (Objects.nonNull(logVO.getInitiator()) && Objects.equals(logVO.getResult(), PROCESSING) && (Objects.equals(logVO.getInitiator().buildAssigneeId_1_2_1(), le130Assignee) || logVO.getInitiator().buildAssigneeId().contains(ge130Assignee))) { authorizedButtons.addAll(chooseButtons(logVO, CONFIG_BUTTON_TYPE_INITIATOR)); @@ -1129,7 +1410,9 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic // 比对历史审批人 logVO.getTaskDetails().stream() - .filter(i -> Objects.equals(i.getNodeType(), NODE_TASK) || Objects.equals(i.getNodeType(), NODE_BUSINESS)) + .filter(i -> Objects.equals(i.getNodeType(), NODE_TASK) + || Objects.equals(i.getNodeType(), NODE_BUSINESS) + || Objects.equals(i.getNodeType(), NODE_SIGN)) .filter(i -> !Objects.equals(PROCESSING, i.getResult())) .map(BpmnTaskInstanceLogVO::getAssigneeSnapshot) .filter(Objects::nonNull) @@ -1148,15 +1431,22 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic logVO.setCurrentUserButtons(authorizedButtons); // 有权限访问的自定义按钮 - List customButtonKeys = authorizedButtons.stream() - .filter(i -> Objects.equals(i.getType(), "CUSTOM")) + logVO.setCustomHiddenButtons(getHiddenCustomButtons(logVO, authorizedButtons)); + } + + private List getHiddenCustomButtons(BpmnProcessInstanceLogVO logVO, List bpmnButtonMetaInfos) { + // 有权限访问的自定义按钮 + List customButtonKeys = bpmnButtonMetaInfos.stream() + .filter(i -> Objects.equals(i.getType(), BpmnButtonMetaInfo.BUTTON_TYPE_CUSTOM)) + .filter(i -> Boolean.FALSE.equals(i.getDisabled()) && Boolean.TRUE.equals(i.getChecked())) .map(BpmnButtonMetaInfo::getBtnKey) - .distinct().collect(Collectors.toList()); - List customButtons = logVO.getDefaultButtonConf().getInitiator().stream() - .filter(i -> Objects.equals(i.getType(), "CUSTOM")) + .distinct() + .collect(Collectors.toList()); + return logVO.getDefaultButtonConf().getInitiator() + .stream() + .filter(i -> Objects.equals(i.getType(), BpmnButtonMetaInfo.BUTTON_TYPE_CUSTOM)) .filter(i -> !customButtonKeys.contains(i.getBtnKey())) .collect(Collectors.toList()); - logVO.setCustomHiddenButtons(customButtons); } /** @@ -1197,6 +1487,23 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic return mergeButtons; } + /** + * 过滤Disabled=false && Checked=true + * + * @param bpmnButtonMetaInfos 按钮列表 + * @return 有效的按钮列表 + */ + private List filterValuableButtons(List bpmnButtonMetaInfos) { + if (CollectionUtils.isEmpty(bpmnButtonMetaInfos)) { + return Collections.emptyList(); + } + return bpmnButtonMetaInfos.stream() + .filter(i -> !i.getDisabled()) + .filter(BpmnButtonMetaInfo::getChecked) + .distinct() + .collect(Collectors.toList()); + } + public static String getLe130Assignee(BpmnTaskDelegateAssigner visitor) { return visitor.getTenantId() + "|" + visitor.getAssignee() + "|" + visitor.getAssigneeType(); } @@ -1257,7 +1564,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic }); } - private static void getFutureTasks(List forecasting, List tasks) { + private static void getFutureTasks + (List forecasting, List tasks) { ListUtils.emptyIfNull(forecasting).forEach(e -> { BpmnTaskInstanceLogVO build = BpmnTaskInstanceLogVO.builder() .taskDefinitionKey(e.getId()) @@ -1270,15 +1578,29 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic if (Objects.nonNull(e.getApprovalMethod())) { switch (e.getApprovalMethod()) { case bizSpecify: + build.setOperationDesc("动态审批人"); + break; case nobody: - build.setOperationDesc("待处理"); + build.setOperationDesc("系统处理"); break; case autoPassed: + build.setOperationDesc("无需审批人,自动同意"); + break; case autoRejection: + build.setOperationDesc("无需审批人,自动驳回"); + break; + case autoPassed_empty: + build.setOperationDesc("未找到审批人,自动同意"); + break; + case autoRejection_empty: + build.setOperationDesc("未找到审批人,自动驳回"); + break; + case transferToAdmin: + build.setOperationDesc("找不到审批人且转交管理员失败,自动中止"); break; case human: if (Objects.equals(e.getNodeMode(), EXCEPTIONAL)) { - build.setOperationDesc("节点异常"); + build.setOperationDesc(""); } else { int countPerson = e.getForecastAssigners().size(); if (Objects.equals(BpmnFlowNodeMode.AND, e.getNodeMode())) { @@ -1333,8 +1655,10 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic i.setForecastAssignees(assigners); i.setButtonConf(e.getButtonConf()); // 根据当前登录人重设聚合后的节点 taskId - assigners.stream().filter(user -> Objects.equals(user.getPersonId(), visitor.getPersonId())).findFirst() - .ifPresent(user -> i.setTaskId(e.getTaskId())); + if (Objects.nonNull(visitor)) { + assigners.stream().filter(user -> Objects.equals(user.getPersonId(), visitor.getPersonId())).findFirst() + .ifPresent(user -> i.setTaskId(e.getTaskId())); + } }); } else { tasks.add(BpmnTaskInstanceLogVO.builder() @@ -1354,16 +1678,17 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic .imageList(getAttachmentByType(attachmentByTaskMap, e.getTaskId(), AttachmentTypeEnum.image)) .fileList(getAttachmentByType(attachmentByTaskMap, e.getTaskId(), AttachmentTypeEnum.file)) .signatureUrl(getAttachmentByType(attachmentByTaskMap, e.getTaskId(), AttachmentTypeEnum.signature).stream().findFirst().orElse(new AttachmentDTO()).getUrl()) + .signature(e.getSignature()) .assigneeSnapshot(Objects.equals(e.getNodeType(), BpmnFlowNodeType.NODE_CARBON_COPY.getType()) ? null : assigner) .forecastAssignees(Objects.equals(e.getNodeType(), BpmnFlowNodeType.NODE_CARBON_COPY.getType()) ? ListUtils.emptyIfNull(e.getAssigneeFull()) : Collections.emptyList()) .build()); } }); - } - public List getAttachmentByType(Map> attachmentByTaskMap, String taskId, AttachmentTypeEnum type) { + public List getAttachmentByType(Map> attachmentByTaskMap, String + taskId, AttachmentTypeEnum type) { return ListUtils.emptyIfNull(attachmentByTaskMap.get(taskId)).stream() .filter(attachment -> Objects.equals(type.getType(), attachment.getType())) .map(e -> AttachmentDTO.builder() @@ -1376,4 +1701,87 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic .collect(Collectors.toList()); } + @Override + public String getModelIdByProcessInstanceId(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(processInstanceId) + .singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new WorkflowEngineException(PROCESS_INSTANCE_ID_NOT_EXISTS, processInstanceId); + } + return getModelIdByProcessDefinitionId(null, historicProcessInstance.getProcessDefinitionId()); + } + + @Override + public String getModelIdByProcessDefinitionId(String processInstanceId, String processDefinitionId) { + String key, tenantId; + if (StringUtils.hasText(processInstanceId)) { + HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(processInstanceId) + .singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new WorkflowEngineException(PROCESS_INSTANCE_ID_NOT_EXISTS, processInstanceId); + } + key = historicProcessInstance.getProcessDefinitionKey(); + tenantId = historicProcessInstance.getTenantId(); + } else { + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId); + if (Objects.isNull(processDefinition)) { + throw new WorkflowEngineException(PROCESS_DEFINITION_ID_NOT_EXISTS, processDefinitionId); + } + key = processDefinition.getKey(); + tenantId = processDefinition.getTenantId(); + } + return repositoryService.createModelQuery() + .modelKey(key) + .list().stream() + .filter(i -> (Objects.equals(i.getTenantId(), tenantId) || Objects.equals(i.getTenantId(), NO_TENANT_ID)) + && (Objects.equals(i.getCategory(), BPMN_FILE_SUFFIX) || Objects.equals(i.getCategory(), i.getKey())) + && i.getMetaInfo().contains(MODEL_TYPE_PROCESS)) + .sorted((a, b) -> { + if (Objects.equals(a.getTenantId(), tenantId)) { + return -1; + } else if (Objects.equals(b.getTenantId(), tenantId)) { + return 1; + } + return 0; + }) + .findFirst() + .orElseThrow(() -> new WorkflowEngineException(MODEL_NOT_EXISTS)) + .getId(); + } + + @Override + public void overrideFormVariables(FormVariablesUpdateDTO dto) { + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + commandExecutor.execute(new CustomOverrideFormVariablesByLatestInstanceCmd(dto.getProcessInstanceId(), dto.getFormVariables())); + } + + @Override + public List processInstanceSelectDocs(ProcessDocQueryDTO dto) { + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + List docs = commandExecutor.execute(new CustomGetModelDocsCmd(dto.getProcessInstanceId(), true, extAxModelDocMapper, extAxReModelService)); + + Map readStatusMap = new HashMap<>(); + if (Objects.nonNull(dto.getAssigner())) { + readStatusMap.putAll(extAxReadRecordService.queryReadStatus(ApproverReadStatusDTO.builder() + .processInstanceId(dto.getProcessInstanceId()) + .assigner(dto.getAssigner()) + .build()).stream().collect(Collectors.toMap(SimpleDocDTO::getId, SimpleDocDTO::getReadStatus, (s, t) -> s))); + } + ExtAxProcessSign processSign = extAxProcessSignService.findByProcessInstanceId(dto.getProcessInstanceId()); + Map archiveFileMap = new HashMap<>(); + if (Objects.nonNull(processSign)) { + archiveFileMap.putAll(processSign.getFileArchive().stream().collect(Collectors.toMap(SignFileDTO::getId, Function.identity()))); + } + + return BeanMapper.copyList(docs, DocPendingVO.class, (s, t) -> { + SignFileDTO archive = archiveFileMap.getOrDefault(t.getId(), null); + if (Objects.nonNull(archive)) { + t.setFileRelationId(archive.getFileCode()); + t.setFileKey(archive.getFileKey()); + } + t.setReadStatus(readStatusMap.getOrDefault(t.getId(), false)); + }); + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessModelServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessModelServiceImpl.java index 3d0ff6b6f..64819f436 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessModelServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessModelServiceImpl.java @@ -1,19 +1,29 @@ package cn.axzo.workflow.core.service.impl; import cn.axzo.framework.jackson.utility.JSON; +import cn.axzo.workflow.common.enums.ExtModelStateFieldEnum; +import cn.axzo.workflow.common.enums.ModelBizTypeEnum; import cn.axzo.workflow.common.exception.WorkflowEngineException; import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonModel; 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; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelDetailVO; -import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.model.response.category.CategoryItemVO; +import cn.axzo.workflow.common.model.response.print.PrintModelDTO; import cn.axzo.workflow.core.common.utils.BpmnJsonConverterUtil; import cn.axzo.workflow.core.common.utils.ContextHolder; +import cn.axzo.workflow.core.conf.CustomEventManager; +import cn.axzo.workflow.core.engine.event.DocChangeEventImpl; +import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc; +import cn.axzo.workflow.core.service.AggregateModelService; import cn.axzo.workflow.core.service.BpmnProcessDefinitionService; import cn.axzo.workflow.core.service.BpmnProcessModelService; +import cn.axzo.workflow.core.service.CategoryService; +import cn.axzo.workflow.core.service.ExtAxModelDocService; import cn.axzo.workflow.core.service.ExtAxReModelService; import cn.axzo.workflow.core.service.converter.BpmnModelConverter; import cn.hutool.json.JSONUtil; @@ -45,17 +55,18 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_ID_NOT_EXISTS; -import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_IS_DISABLE; import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_KEY_EXISTS; import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_KEY_NOT_EXISTS; import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_NOT_EXISTS; import static cn.axzo.workflow.common.code.BpmnProcessDefinitionRespCode.PROCESS_DEFINITION_BPMN_NOT_EXISTS; import static cn.axzo.workflow.common.constant.BpmnConstants.BPMN_FILE_SUFFIX; +import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; import static cn.axzo.workflow.common.constant.BpmnConstants.DISABLED; import static cn.axzo.workflow.common.constant.BpmnConstants.ENABLED; import static cn.axzo.workflow.common.constant.MetaInfoConstants.MODEL_DESCRIPTION; @@ -80,7 +91,15 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { @Resource private BpmnModelConverter bpmnModelConverter; @Resource + private CategoryService categoryService; + @Resource private String serviceVersion; + @Resource + private ExtAxModelDocService extAxModelDocService; + @Resource + private CustomEventManager eventPublisher; + @Resource + private AggregateModelService aggregateModelService; @Override public BpmPageResult getModelPage(BpmnModelSearchDTO dto) { @@ -130,7 +149,7 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { baseQuerySql.append(")"); } if (Objects.nonNull(dto.getAgent())) { - if (dto.getAgent()) { + if (Boolean.TRUE.equals(dto.getAgent())) { baseQuerySql.append(sqlConnectors(baseQuerySql)) .append(" TENANT_ID_ != '' "); if (!CollectionUtils.isEmpty(dto.getTenantIds())) { @@ -185,16 +204,20 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { } })); - if (Objects.isNull(dto.getStatus())) { - extAxReModelService.listByModelIds(vos.stream().map(BpmnModelDetailVO::getId).collect(Collectors.toList())) - .forEach(i -> vos.forEach(j -> { - if (Objects.equals(i.getModelId(), j.getId())) { - j.setStatus(i.getStatus()); - } - })); - } else { - vos.forEach(i -> i.setStatus(dto.getStatus())); - } +// if (Objects.isNull(dto.getStatus())) { + extAxReModelService.listByModelIds(vos.stream().map(BpmnModelDetailVO::getId).collect(Collectors.toList())) + .forEach(i -> vos.forEach(j -> { + if (Objects.equals(i.getModelId(), j.getId())) { + j.setStatus(i.getStatus()); + j.setPrintStatus(i.getPrintStatus()); + j.setPrintFileName(i.getPrintFileName()); + j.setPrintTemplateName(i.getPrintTemplateName()); + j.setPrintTemplateConfig(i.getPrintTemplateConfig()); + } + })); +// } else { +// vos.forEach(i -> i.setStatus(dto.getStatus())); +// } return new BpmPageResult<>(vos, countSqlQuery.count()); } @@ -244,14 +267,18 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { model.setKey(dto.getKey()); model.setTenantId(dto.getTenantId()); repositoryService.saveModel(model); + + Optional optCategory = categoryService.get(BPM_MODEL_CATEGORY, dto.getKey()); //存储Bpmn协议 - if (Objects.nonNull(dto.getJsonModel())) { + if (Objects.nonNull(dto.getJsonModel()) && optCategory.isPresent()) { BpmnModel bpmnModel = BpmnJsonConverterUtil.convertToBpmn(dto.getJsonModel().getNode(), + ModelBizTypeEnum.valueOfType(optCategory.get().getRemark()), dto.getKey(), dto.getName(), Objects.nonNull(dto.getFormJsonModel()) ? dto.getKey() : null, dto.getDescription(), dto.getJsonModel().getApproveConf(), + dto.getJsonModel().getSignConf(), dto.getJsonModel().getNoticeConf(), dto.getJsonModel().getButtonConf(), dto.getJsonModel().getFieldConf(), @@ -335,14 +362,17 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { originModel.setTenantId(dto.getTenantId()); repositoryService.saveModel(originModel); + Optional optCategory = categoryService.get(BPM_MODEL_CATEGORY, dto.getKey()); //存储Bpmn协议 - if (Objects.nonNull(dto.getJsonModel())) { + if (Objects.nonNull(dto.getJsonModel()) && optCategory.isPresent()) { BpmnModel bpmnModel = BpmnJsonConverterUtil.convertToBpmn(dto.getJsonModel().getNode(), + ModelBizTypeEnum.valueOfType(optCategory.get().getRemark()), dto.getKey(), dto.getName(), Objects.nonNull(dto.getFormJsonModel()) ? dto.getKey() : null, dto.getDescription(), dto.getJsonModel().getApproveConf(), + dto.getJsonModel().getSignConf(), dto.getJsonModel().getNoticeConf(), dto.getJsonModel().getButtonConf(), dto.getJsonModel().getFieldConf(), @@ -398,7 +428,7 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { model.setDeploymentId(definition.getDeploymentId()); model.setCategory(BPMN_FILE_SUFFIX); this.repositoryService.saveModel(model); - extAxReModelService.changeStatus(model.getId(), ENABLED); + extAxReModelService.changeStatus(model.getId(), ENABLED, ExtModelStateFieldEnum.status); return definition.getId(); } @@ -457,24 +487,50 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { throw new WorkflowEngineException(MODEL_ID_NOT_EXISTS, processModelId); } updateProcessDefinitionSuspended(model.getDeploymentId()); - extAxReModelService.changeStatus(model.getId(), DISABLED); + extAxReModelService.changeStatus(model.getId(), DISABLED, ExtModelStateFieldEnum.status); } @Override @Transactional(rollbackFor = Throwable.class) public void changeStatus(String modelId, Integer status, BpmnTaskDelegateAssigner operator) { Model model = this.repositoryService.getModel(modelId); + if (Objects.isNull(model)) { + return; + } // 公共模型不会真实的取消部署 if (!Objects.equals("", model.getTenantId())) { if (Objects.equals(ENABLED, status)) { - deployBpmModelById(modelId, null, operator); + aggregateModelService.deployBpmnAndFormModel(modelId, null, operator); +// deployBpmModelById(modelId, null, operator); } else { unDeployBpmModelById(modelId, null, operator); } } - extAxReModelService.changeStatus(modelId, status); + extAxReModelService.changeStatus(modelId, status, ExtModelStateFieldEnum.status); + + List oldSettings = extAxModelDocService.querySetting(model.getKey(), model.getTenantId()); + List newSettings = extAxModelDocService.querySetting(model.getKey(), ""); + DocChangeEventImpl event; + if (Objects.equals(ENABLED, status)) { + event = new DocChangeEventImpl(model.getKey(), model.getTenantId(), newSettings, oldSettings); + } else { + event = new DocChangeEventImpl(model.getKey(), model.getTenantId(), oldSettings, newSettings); + } + // 发送文档变更事件 + eventPublisher.publishEvent(event); + } + @Override + public void changePrintStatus(String modelId, Integer status, BpmnTaskDelegateAssigner assignee) { + Model model = this.repositoryService.getModel(modelId); + if (Objects.isNull(model)) { + return; + } + extAxReModelService.changeStatus(modelId, status, ExtModelStateFieldEnum.printStatus); + } + + @Override public List getModelCategoryList() { List list = repositoryService.createModelQuery().deployed().list(); @@ -493,6 +549,16 @@ public class BpmnProcessModelServiceImpl implements BpmnProcessModelService { return list.stream().map(Model::getTenantId).distinct().collect(Collectors.toList()); } + @Override + public void printTemplateConfig(PrintTemplateConfigUpsertDTO dto) { + extAxReModelService.printTemplateConfig(dto); + } + + @Override + public PrintModelDTO getPrintTemplateConfig(String modelId) { + return extAxReModelService.getPrintTemplateConfig(modelId); + } + private void updateProcessDefinitionSuspended(String deploymentId) { if (StringUtils.hasLength(deploymentId)) { ProcessDefinition oldDefinition = diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java index 0bf4c62d0..16e3bc22b 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessTaskServiceImpl.java @@ -6,6 +6,7 @@ import cn.axzo.workflow.common.enums.BpmnFlowNodeType; import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; import cn.axzo.workflow.common.exception.WorkflowEngineException; import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO; @@ -29,6 +30,7 @@ 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.core.common.utils.BpmnMetaParserHelper; +import cn.axzo.workflow.core.conf.SupportRefreshProperties; import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskAsyncCmd; import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskCmd; import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskWithFormAsyncCmd; @@ -42,6 +44,8 @@ import cn.axzo.workflow.core.engine.cmd.CustomCountersignUserTaskCmd; import cn.axzo.workflow.core.engine.cmd.CustomCreateDummyTaskCmd; import cn.axzo.workflow.core.engine.cmd.CustomRejectionTaskAsyncCmd; import cn.axzo.workflow.core.engine.cmd.CustomRejectionTaskCmd; +import cn.axzo.workflow.core.engine.cmd.CustomRemindTaskAsyncCmd; +import cn.axzo.workflow.core.engine.cmd.CustomRemindTaskCmd; import cn.axzo.workflow.core.engine.cmd.CustomTransferUserTaskAsyncCmd; import cn.axzo.workflow.core.engine.cmd.CustomTransferUserTaskCmd; import cn.axzo.workflow.core.engine.event.MessagePushEventBuilder; @@ -50,7 +54,6 @@ import cn.axzo.workflow.core.engine.event.MessagePushEventType; import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst; import cn.axzo.workflow.core.service.BpmnProcessDefinitionService; import cn.axzo.workflow.core.service.BpmnProcessTaskService; -import cn.axzo.workflow.core.service.ExtAxBpmnFormRelationService; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import cn.axzo.workflow.core.service.ExtAxProcessLogService; import cn.axzo.workflow.core.service.converter.BpmnHistoricAttachmentConverter; @@ -66,6 +69,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.flowable.bpmn.model.BaseElement; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.Process; import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.common.engine.impl.interceptor.CommandExecutor; @@ -82,9 +86,7 @@ import org.flowable.engine.impl.util.ProcessDefinitionUtil; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.task.Attachment; import org.flowable.engine.task.Comment; -import org.flowable.form.api.FormDefinition; import org.flowable.form.api.FormInfo; -import org.flowable.form.api.FormRepositoryService; import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.task.api.Task; import org.flowable.task.api.TaskInfo; @@ -124,6 +126,8 @@ import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_TASK_NOT import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE; import static cn.axzo.workflow.common.code.BpmnTaskRespCode.FIND_TASK_BY_PERSON_ID_ERROR; import static cn.axzo.workflow.common.code.BpmnTaskRespCode.REACHED_BACKED_MAXIMUM_NUM; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS; +import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_HAS_BEEN_COMPLETE; import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_REMIND_ERROR_NOT_EXISTS; import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE; import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_COMMENT_EXT; @@ -137,6 +141,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VER import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CARBON_COPY; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY; +import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_SIGN; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED; @@ -148,6 +153,7 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.valueO import static cn.axzo.workflow.common.util.BpmnNativeQueryUtil.countSql; import static cn.axzo.workflow.common.util.BpmnNativeQueryUtil.sqlConnectors; import static cn.axzo.workflow.core.common.utils.BpmnCollectionUtils.convertSet; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature; @Service @Slf4j @@ -186,6 +192,10 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { private BpmnProcessDefinitionService bpmnProcessModelService; @Resource private ExtAxProcessLogService extAxProcessLogService; + @Resource + private ExtAxProcessLogService processLogService; + @Resource + private SupportRefreshProperties refreshProperties; @Override public BpmPageResult getTodoTaskPage(BpmnTaskPageSearchDTO dto) { @@ -350,7 +360,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { public void approveTaskWithForm(BpmnTaskAuditWithFormDTO dto) { // 表单级别暂时只允许同步 CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); - if(Boolean.TRUE.equals(dto.getAsync())) { + if (Boolean.TRUE.equals(dto.getAsync())) { commandExecutor.execute(new CustomApproveTaskWithFormAsyncCmd(dto)); } else { commandExecutor.execute(new CustomApproveTaskWithFormCmd(dto)); @@ -360,7 +370,11 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { @Override @Transactional(rollbackFor = Exception.class) public void backTask(BpmnTaskBackAuditDTO dto) { - List backOptionalNodes = getBackOptionalNodes(dto.getTaskId()); + Task task = processEngineConfiguration.getTaskService().createTaskQuery().taskId(dto.getTaskId()).singleResult(); + if (task == null) { + throw new WorkflowEngineException(PROCESS_TASK_NOT_EXISTS); + } + List backOptionalNodes = getBackOptionalNodes(task.getProcessInstanceId(), task.getTaskDefinitionKey()); if (CollectionUtils.isEmpty(backOptionalNodes)) { throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId()); } @@ -389,17 +403,98 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { if (Boolean.TRUE.equals(dto.getAsync())) { commandExecutor.execute(new CustomBackTaskAsyncCmd(dto)); } else { - commandExecutor.execute(new CustomBackTaskCmd(dto)); + commandExecutor.execute(new CustomBackTaskCmd(CustomBackTaskCmd.CustomBackParamsDto.builder() + .targetTaskIds(Collections.singletonList(dto.getTaskId())) + .advice(dto.getAdvice()) + .operationDesc(dto.getOperationDesc()) + .attachmentList(dto.getAttachmentList()) + .operator(dto.getApprover()) + .processInstanceId(task.getProcessInstanceId()) + .currentActivityId(task.getTaskDefinitionKey()) + .toActivityId(dto.getToActivityId()) + .validateApprover(true) + .build())); } } @Override - public List getBackOptionalNodes(String taskId) { + public void systemBackTask(BpmnNodeBackSystemOperateDTO dto) { + //需要查询当前流程是否停留在当前节点 + //需要查询退回的节点是否可达 + //然后进行退回操作 + List taskList = processEngineConfiguration.getHistoryService() + .createHistoricTaskInstanceQuery() + .taskDefinitionKey(dto.getCurrentActivityId()) + .processInstanceId(dto.getProcessInstanceId()) + .list(); + if (CollectionUtils.isEmpty(taskList)) { + throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS); + } + List valuableTasks = taskList.stream() + .filter(t -> Objects.isNull(t.getEndTime())) + .filter(t -> { + if (CollectionUtils.isNotEmpty(dto.getTargetTaskIds())) { + return dto.getTargetTaskIds().contains(t.getId()); + } + return true; + }) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(valuableTasks)) { + throw new WorkflowEngineException(TASK_HAS_BEEN_COMPLETE); + } + List backOptionalNodes = getBackOptionalNodes(valuableTasks.get(0).getProcessInstanceId(), valuableTasks.get(0).getTaskDefinitionKey()); + if (CollectionUtils.isEmpty(backOptionalNodes)) { + throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId()); + } + List activityList = backOptionalNodes.stream().map(BpmnOptionalNodeDTO::getProcessActivityId).collect(Collectors.toList()); + if (!activityList.contains(dto.getToActivityId())) { + throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId()); + } + Optional instOpt = backOptionalNodes.stream() + .map(BpmnOptionalNodeDTO::getProcessInstanceId) + .filter(StringUtils::hasText) + .findAny(); + if (instOpt.isPresent()) { + ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO(); + searchDTO.setProcessInstanceId(instOpt.get()); + List extAxHiTaskInsts = extAxHiTaskInstService.queryList(searchDTO); + if (CollectionUtils.isNotEmpty(extAxHiTaskInsts)) { + long backOpeCount = extAxHiTaskInsts.stream() + .filter(ext -> BACKED.getStatus().equals(ext.getStatus())) + .count(); + if (backOpeCount > MAX_BACKED_OPERATE_COUNT) { + throw new WorkflowEngineException(REACHED_BACKED_MAXIMUM_NUM, String.valueOf(MAX_BACKED_OPERATE_COUNT)); + } + } + } + BpmnTaskDelegateAssigner operator = dto.getOperator(); + if (operator == null) { + operator = new BpmnTaskDelegateAssigner("系统", "system", taskList.get(0).getTenantId()); + } + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + commandExecutor.execute(new CustomBackTaskCmd(CustomBackTaskCmd.CustomBackParamsDto.builder() + .targetTaskIds(valuableTasks.stream().map(TaskInfo::getId).collect(Collectors.toList())) + .advice(dto.getAdvice()) + .operationDesc(dto.getOperationDesc()) + .attachmentList(dto.getAttachmentList()) + .operator(operator) + .processInstanceId(valuableTasks.get(0).getProcessInstanceId()) + .currentActivityId(dto.getCurrentActivityId()) + .toActivityId(dto.getToActivityId()) + .build())); + } + + @Override + public List getBackOptionalNodesByTaskId(String taskId) { Task task = processEngineConfiguration.getTaskService().createTaskQuery().taskId(taskId).singleResult(); if (task == null) { throw new WorkflowEngineException(PROCESS_TASK_NOT_EXISTS); } - String processInstanceId = task.getProcessInstanceId(); + return this.getBackOptionalNodes(task.getProcessInstanceId(), task.getTaskDefinitionKey()); + } + + @Override + public List getBackOptionalNodes(String processInstanceId, String currentActivityId) { //1.获取当前的流程实例 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); if (processInstance == null) { @@ -408,7 +503,10 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { } List tasks = this.getHistoricTaskListByProcessInstanceId(processInstanceId, null); tasks = tasks.stream() - .filter(t -> t.getNodeType() == NODE_STARTER || t.getNodeType() == NODE_TASK || t.getNodeType() == NODE_BUSINESS) + .filter(t -> t.getNodeType() == NODE_STARTER + || t.getNodeType() == NODE_TASK + || t.getNodeType() == NODE_BUSINESS + || t.getNodeType() == NODE_SIGN) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(tasks)) { return Collections.emptyList(); @@ -458,17 +556,17 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { AtomicInteger index = new AtomicInteger(0); List resultList = valuableList .stream() - .filter(pair -> !task.getTaskDefinitionKey().equals(pair.getLeft())) //排除当前节点 + .filter(pair -> !currentActivityId.equals(pair.getLeft())) //排除当前节点 .map(pair -> flowElementMap.get(pair.getLeft())) .filter(flowElement -> { BpmnFlowNodeType currentNodeType = BpmnMetaParserHelper.getNodeType(flowElement).orElse(NODE_EMPTY); - return currentNodeType == NODE_TASK || currentNodeType == NODE_BUSINESS; + return currentNodeType == NODE_TASK || currentNodeType == NODE_BUSINESS || currentNodeType == NODE_SIGN; }) .filter(flowElement -> !NODE_STARTER.getType().equals(flowElement.getId())) .map(flowElement -> BpmnOptionalNodeDTO .builder() - .processInstanceId(task.getProcessInstanceId()) - .processDefinitionId(task.getProcessDefinitionId()) + .processInstanceId(processInstanceId) + .processDefinitionId(processInstance.getProcessDefinitionId()) .processActivityId(flowElement.getId()) .processActivityName(flowElement.getName()) .processNodeDesc(flowElement.getName()) @@ -493,7 +591,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { @Transactional(rollbackFor = Exception.class) public void rejectTask(BpmnTaskAuditDTO dto) { CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); - if (dto.getAsync()) { + if (Boolean.TRUE.equals(dto.getAsync())) { commandExecutor.execute(new CustomRejectionTaskAsyncCmd(dto)); } else { commandExecutor.execute(new CustomRejectionTaskCmd(dto, extAxHiTaskInstService)); @@ -779,44 +877,12 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService { @Override public void remindTask(BpmnTaskRemindDTO dto) { - TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(dto.getProcessInstanceId()); - if (StringUtils.hasLength(dto.getTaskDefinitionKey())) { - taskQuery.taskDefinitionKey(dto.getTaskDefinitionKey()); + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + if (Boolean.TRUE.equals(dto.getAsync())) { + commandExecutor.execute(new CustomRemindTaskAsyncCmd(dto)); + } else { + commandExecutor.execute(new CustomRemindTaskCmd(dto.getTerminalType(), dto.getProcessInstanceId(), dto.getTaskDefinitionKey(), dto.getRemindTypes(), refreshProperties)); } - List list = taskQuery.active().list(); - if (CollectionUtils.isEmpty(list)) { - throw new WorkflowEngineException(TASK_REMIND_ERROR_NOT_EXISTS); - } - - if (CollectionUtils.isEmpty(dto.getRemindTypes())) { - return; - } - ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); - FlowableEventDispatcher eventDispatcher = processEngineConfiguration.getEventDispatcher(); - ProcessInstance processInstance = - runtimeService.createProcessInstanceQuery().processInstanceId(dto.getProcessInstanceId()).singleResult(); - Optional noticeConfig = - BpmnMetaParserHelper.getNoticeConfig(ProcessDefinitionUtil.getProcess(processInstance.getProcessDefinitionId())); - List assigners = - (List) runtimeService.getVariable(dto.getProcessInstanceId(), - INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + dto.getTaskDefinitionKey()); - - // 过滤出未审批的任何,用选择的方式去发送消息 - dto.getRemindTypes().forEach(type -> { - list.forEach(task -> { - assigners.stream().filter(i -> Objects.equals(task.getAssignee(), i.buildAssigneeId())).findAny().ifPresent(assigner -> { - MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.valueOf(type), - Lists.newArrayList(assigner), noticeConfig.orElse(null), - processInstance.getProcessInstanceId(), - processInstance.getProcessDefinitionKey(), - processInstance.getTenantId(), task.getId()); - event.setProcessInstanceId(processInstance.getProcessInstanceId()); - event.setTenantId(processInstance.getTenantId()); - event.setTaskId(task.getId()); - eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey()); - }); - }); - }); } @Override diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryConfigServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryConfigServiceImpl.java index 63383b0ee..dd65cf121 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryConfigServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryConfigServiceImpl.java @@ -1,31 +1,45 @@ package cn.axzo.workflow.core.service.impl; +import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; 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.CategoryGroupVarSearchDto; +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarUpsertDto; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.category.CategoryConfigItemVO; -import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.response.category.CategoryGroupVarItemVo; import cn.axzo.workflow.core.repository.entity.ExtAxDict; import cn.axzo.workflow.core.repository.entity.ExtAxDictConf; +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroup; +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroupVariable; import cn.axzo.workflow.core.repository.mapper.ExtAxDictConfMapper; +import cn.axzo.workflow.core.repository.mapper.ExtAxDictGroupMapper; +import cn.axzo.workflow.core.repository.mapper.ExtAxDictGroupVariableMapper; import cn.axzo.workflow.core.repository.mapper.ExtAxDictMapper; import cn.axzo.workflow.core.service.CategoryConfigService; import cn.axzo.workflow.core.service.converter.CategoryConfigConverter; +import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.ListUtils; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.stream.Collectors; -import static cn.axzo.workflow.common.code.CategoryRespCode.CATEGORY_CONFIG_EXISTS; +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.*; +import static cn.axzo.workflow.common.code.CategoryRespCode.*; /** * 分类黑白名单配置服务 diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryGroupServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryGroupServiceImpl.java new file mode 100644 index 000000000..1bec90c76 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryGroupServiceImpl.java @@ -0,0 +1,175 @@ +package cn.axzo.workflow.core.service.impl; + +import cn.axzo.basics.common.constant.enums.TableIsDeleteEnum; +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import cn.axzo.framework.domain.ServiceException; +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarSearchDto; +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarUpsertDto; +import cn.axzo.workflow.common.model.response.category.CategoryGroupVarItemVo; +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroup; +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroupVariable; +import cn.axzo.workflow.core.repository.mapper.ExtAxDictGroupMapper; +import cn.axzo.workflow.core.service.CategoryGroupService; +import cn.axzo.workflow.core.service.CategoryGroupVariableService; +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CategoryGroupServiceImpl extends ServiceImpl implements CategoryGroupService { + + private final CategoryGroupVariableService categoryGroupVariableService; + + @Override + public List searchGroupAndVarList(CategoryGroupVarSearchDto dto) { + if ((dto.getDictId() == null || dto.getDictId() <= 0) && StringUtils.isBlank(dto.getCategory())) { + throw new ServiceException("dictId和category不能同时为空"); + } + List extAxDictGroups = this.lambdaQuery() + .eq(Objects.nonNull(dto.getDictId()) && dto.getDictId() > 0, ExtAxDictGroup::getDictId, dto.getDictId()) + .inSql((Objects.isNull(dto.getDictId()) || dto.getDictId() <= 0) && StringUtils.isNotBlank(dto.getCategory()), ExtAxDictGroup::getDictId, + String.format("SELECT id FROM ext_ax_dict WHERE value = '%s'", dto.getCategory())) + .orderByAsc(ExtAxDictGroup::getOrdinal) + .list(); + if (CollectionUtils.isEmpty(extAxDictGroups)) { + return Collections.emptyList(); + } + List groupVars = categoryGroupVariableService.lambdaQuery() + .eq(Objects.nonNull(dto.getDictId()), ExtAxDictGroupVariable::getDictId, dto.getDictId()) + .eq(ExtAxDictGroupVariable::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .list(); + Map> varMap = ListUtils.emptyIfNull(groupVars).stream().collect(Collectors.groupingBy(ExtAxDictGroupVariable::getGroupId)); + return extAxDictGroups.stream() + .map(group -> { + CategoryGroupVarItemVo categoryGroupVarItemVo = BeanUtil.copyProperties(group, CategoryGroupVarItemVo.class); + List categoryVarItemVos = BeanUtil.copyToList(ListUtils.emptyIfNull(varMap.get(group.getId())), CategoryGroupVarItemVo.CategoryVarItemVo.class); + categoryGroupVarItemVo.setVars(categoryVarItemVos); + return categoryGroupVarItemVo; + }).collect(Collectors.toList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean upsertGroupAndVars(CategoryGroupVarUpsertDto dto) { + if (dto.getDictId() == null || dto.getDictId() <= 0) { + throw new ServiceException("dictId 不能为空且必须大于0"); + } + if (ListUtils.emptyIfNull(dto.getGroupVos()).stream() + .collect(Collectors.groupingBy(CategoryGroupVarUpsertDto.CategoryGroupUpsertVo::getOrdinal)) + .values() + .stream() + .anyMatch(gl -> gl.size() > 1)) { + throw new ServiceException("分组 ordinal 不允许重复"); + } + List existGroups = this.lambdaQuery() + .eq(ExtAxDictGroup::getDictId, dto.getDictId()) + .eq(ExtAxDictGroup::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .list(); + Map> existGroupMap = ListUtils.emptyIfNull(existGroups).stream().collect(Collectors.groupingBy(BaseEntity::getId)); + List existVars = categoryGroupVariableService.lambdaQuery() + .eq(ExtAxDictGroupVariable::getDictId, dto.getDictId()) + .eq(ExtAxDictGroupVariable::getIsDelete, TableIsDeleteEnum.NORMAL.value) + .list(); + if (CollectionUtils.isEmpty(dto.getGroupVos())) { + if (!CollectionUtils.isEmpty(existGroups)) { + this.removeByIds(existGroups.stream().map(BaseEntity::getId).collect(Collectors.toList())); + } + if (!CollectionUtils.isEmpty(existVars)) { + categoryGroupVariableService.removeByIds(existVars.stream().map(BaseEntity::getId).collect(Collectors.toList())); + } + return Boolean.TRUE; + } + Map> existVarMap = ListUtils.emptyIfNull(existVars).stream().collect(Collectors.groupingBy(BaseEntity::getId)); + List saveOrUpdateGroups = new ArrayList<>(); + List saveOrUpdateVars = new ArrayList<>(); + Map> createVarMap = new HashMap<>(); + for (CategoryGroupVarUpsertDto.CategoryGroupUpsertVo groupVo : dto.getGroupVos()) { + ExtAxDictGroup extAxDictGroup = new ExtAxDictGroup(); + extAxDictGroup.setGroupName(groupVo.getGroupName()); + extAxDictGroup.setDictId(dto.getDictId()); + extAxDictGroup.setParentGroupId(groupVo.getParentGroupId()); + extAxDictGroup.setOrdinal(groupVo.getOrdinal()); + if (groupVo.getId() != null && groupVo.getId() > 0) { + extAxDictGroup.setId(groupVo.getId()); + saveOrUpdateGroups.add(extAxDictGroup); + List vars = groupVo.getVars(); + if (!CollectionUtils.isEmpty(vars)) { + for (CategoryGroupVarUpsertDto.CategoryVarUpsertVo v : vars) { + ExtAxDictGroupVariable extAxDictGroupVariable = BeanUtil.copyProperties(v, ExtAxDictGroupVariable.class); + if (v.getId() != null && v.getId() > 0) { + saveOrUpdateVars.add(extAxDictGroupVariable); + } else { + extAxDictGroupVariable.setDictId(dto.getDictId()); + extAxDictGroupVariable.setGroupId(groupVo.getId()); + extAxDictGroupVariable.setOrdinal(groupVo.getOrdinal()); + extAxDictGroupVariable.setDictId(dto.getDictId()); + createVarMap.computeIfAbsent(groupVo.getOrdinal(), c -> new ArrayList<>()).add(extAxDictGroupVariable); + } + } + } + } else { + saveOrUpdateGroups.add(extAxDictGroup); + List vars = groupVo.getVars(); + if (!CollectionUtils.isEmpty(vars)) { + vars.forEach(v -> { + ExtAxDictGroupVariable extAxDictGroupVariable = BeanUtil.copyProperties(v, ExtAxDictGroupVariable.class); + extAxDictGroupVariable.setOrdinal(v.getOrdinal()); + extAxDictGroupVariable.setDictId(dto.getDictId()); + createVarMap.computeIfAbsent(groupVo.getOrdinal(), c -> new ArrayList<>()).add(extAxDictGroupVariable); + }); + } + } + } + if (!existGroupMap.isEmpty()) { + saveOrUpdateGroups.stream() + .filter(g -> g.getId() != null && g.getId() > 0) + .forEach(g -> existGroupMap.remove(g.getId())); + } + if (!existVarMap.isEmpty()) { + saveOrUpdateVars.stream() + .filter(v -> v.getId() != null && v.getId() > 0) + .forEach(v -> existVarMap.remove(v.getId())); + } + boolean groupSaveResult = this.saveOrUpdateBatch(saveOrUpdateGroups); + if (!groupSaveResult) { + throw new ServiceException("保存失败"); + } + Map> ordinalGroupMap = saveOrUpdateGroups.stream().collect(Collectors.groupingBy(ExtAxDictGroup::getOrdinal)); + createVarMap.forEach((key, value) -> value.forEach(v -> v.setGroupId(ordinalGroupMap.get(key).get(0).getId()))); + saveOrUpdateVars.addAll(createVarMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList())); + //保存变量 + categoryGroupVariableService.saveOrUpdateBatch(saveOrUpdateVars); + //删除多余的group和变量 + if (!existGroupMap.isEmpty()) { + //删除分组 + this.removeByIds(existGroupMap.keySet()); + //删除分组下的变量 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.in("group_id", existGroupMap.keySet()); + categoryGroupVariableService.remove(queryWrapper); + } + //删除多余变量 + if (!existVarMap.isEmpty()) { + categoryGroupVariableService.removeByIds(existVarMap.keySet()); + } + return true; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryGroupVariableServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryGroupVariableServiceImpl.java new file mode 100644 index 000000000..4e20fdf01 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryGroupVariableServiceImpl.java @@ -0,0 +1,15 @@ +package cn.axzo.workflow.core.service.impl; + +import cn.axzo.workflow.core.repository.entity.ExtAxDictGroupVariable; +import cn.axzo.workflow.core.repository.mapper.ExtAxDictGroupVariableMapper; +import cn.axzo.workflow.core.service.CategoryGroupVariableService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CategoryGroupVariableServiceImpl extends ServiceImpl implements CategoryGroupVariableService { +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryServiceImpl.java index 3215f55e4..3de68514c 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/CategoryServiceImpl.java @@ -1,13 +1,13 @@ package cn.axzo.workflow.core.service.impl; import cn.axzo.workflow.common.constant.BpmnConstants; +import cn.axzo.workflow.common.exception.WorkflowEngineException; import cn.axzo.workflow.common.model.request.category.CategoryCreateDTO; import cn.axzo.workflow.common.model.request.category.CategorySearchDTO; 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.common.exception.WorkflowEngineException; import cn.axzo.workflow.core.repository.entity.ExtAxDict; import cn.axzo.workflow.core.repository.mapper.ExtAxDictMapper; import cn.axzo.workflow.core.service.CategoryConfigService; @@ -30,11 +30,11 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; -import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; import static cn.axzo.workflow.common.code.CategoryRespCode.CATEGORY_DATA_ERROR; import static cn.axzo.workflow.common.code.CategoryRespCode.CATEGORY_ID_NOT_EXISTS; import static cn.axzo.workflow.common.code.CategoryRespCode.CATEGORY_NAME_EXISTS; import static cn.axzo.workflow.common.code.CategoryRespCode.CATEGORY_VALUE_EXISTS; +import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; @Service @@ -115,6 +115,9 @@ public class CategoryServiceImpl extends ServiceImpl dict.setTenantId(dto.getTenantId()); dict.setOperatorName(dto.getOperatorName()); dict.setWorkspaceTypeCode(dto.getWorkspaceCodeType()); + dict.setBusinessType(dto.getBusinessType()); + dict.setIcon(dto.getIcon()); + dict.setDisplayInitiateMenu(dto.getDisplayInitiateMenu()); } @Override @@ -156,6 +159,7 @@ public class CategoryServiceImpl extends ServiceImpl .eq(StringUtils.isNotBlank(dto.getWorkspaceTypeCode()), ExtAxDict::getWorkspaceTypeCode, dto.getWorkspaceTypeCode()) .eq(ExtAxDict::getTenantId, dto.getTenantId()) + .eq(dto.getBusinessType() != null, ExtAxDict::getBusinessType, dto.getBusinessType()) .orderByDesc(ExtAxDict::getCreateAt); Page page = dictMapper.selectPage(new Page<>(dto.getPageNo(), dto.getPageSize()), @@ -195,9 +199,7 @@ public class CategoryServiceImpl extends ServiceImpl if (!dictDO.isPresent()) { throw new WorkflowEngineException(CATEGORY_ID_NOT_EXISTS, String.valueOf(id)); } - dictDO.ifPresent(dict -> { - dict.setStatus(state ? 1 : 0); - }); + dictDO.ifPresent(dict -> dict.setStatus(Boolean.TRUE.equals(state) ? 1 : 0)); dictMapper.updateById(dictDO.get()); return true; } @@ -208,9 +210,7 @@ public class CategoryServiceImpl extends ServiceImpl if (!dictDO.isPresent()) { throw new WorkflowEngineException(CATEGORY_ID_NOT_EXISTS, String.valueOf(id)); } - dictDO.ifPresent(dict -> { - dict.setRemark(configType); - }); + dictDO.ifPresent(dict -> dict.setRemark(configType)); dictMapper.updateById(dictDO.get()); return true; } @@ -225,7 +225,10 @@ public class CategoryServiceImpl extends ServiceImpl .eq(StringUtils.isNotBlank(dto.getWorkspaceTypeCode()), ExtAxDict::getWorkspaceTypeCode, dto.getWorkspaceTypeCode()) .eq(ExtAxDict::getTenantId, dto.getTenantId()) - .eq(ExtAxDict::getIsDelete, 0); + .eq(dto.getBusinessType() != null, ExtAxDict::getBusinessType, dto.getBusinessType()) + .eq(ExtAxDict::getIsDelete, 0) + .orderByDesc(Objects.equals(dto.getOrderCreateAt(), "desc"), ExtAxDict::getCreateAt); + ; List extAxDicts = dictMapper.selectList(queryWrapper); return categoryConverter.toVos(extAxDicts); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxDocContentServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxDocContentServiceImpl.java new file mode 100644 index 000000000..9fefbf808 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxDocContentServiceImpl.java @@ -0,0 +1,84 @@ +package cn.axzo.workflow.core.service.impl; + +import cn.axzo.framework.data.mybatisplus.model.BaseEntity; +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.core.repository.entity.ExtAxDocContent; +import cn.axzo.workflow.core.repository.mapper.ExtAxDocContentMapper; +import cn.axzo.workflow.core.service.ExtAxDocContentService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_CONTENT_DATA_ERROR; + +/** + * 文档模板内容 + * + * @author wangli + * @since 2025-03-28 18:47 + */ +@Service +@Slf4j +public class ExtAxDocContentServiceImpl implements ExtAxDocContentService { + @Resource + private ExtAxDocContentMapper extAxDocContentMapper; + + @Override + public List getByIds(List ids) { + return extAxDocContentMapper.selectBatchIds(ids); + } + + @Override + public ExtAxDocContent createContent(String content, FileTypeEnum fileType, Long docId) { + ExtAxDocContent entity = new ExtAxDocContent(); + entity.setContent(StringUtils.hasText(content) ? content : ""); + entity.setFileType(fileType.name()); + entity.setFileId(docId); + extAxDocContentMapper.insert(entity); + return entity; + } + + @Override + public ExtAxDocContent updateContent(String content, FileTypeEnum fileType, String docId) { + ExtAxDocContent entity = extAxDocContentMapper.selectOne(new LambdaQueryWrapper() + .eq(ExtAxDocContent::getId, Long.valueOf(docId))); + if (Objects.nonNull(content)) { + entity.setContent(content); + } + entity.setFileType(fileType.name()); + extAxDocContentMapper.updateById(entity); + return entity; + } + + @Override + public ExtAxDocContent deleteContent(String docId) { + ExtAxDocContent entity = extAxDocContentMapper.selectOne(new LambdaQueryWrapper() + .eq(ExtAxDocContent::getId, docId)); + extAxDocContentMapper.deleteById(entity); + return entity; + } + + @Override + public List batchDeleteContent(List docIds) { + List entities = extAxDocContentMapper.selectList(new LambdaQueryWrapper() + .in(ExtAxDocContent::getId, docIds)); + extAxDocContentMapper.deleteBatchIds(entities.stream().map(BaseEntity::getId).collect(Collectors.toList())); + return entities; + } + + @Override + public String getContent(Long docId) { + ExtAxDocContent content = extAxDocContentMapper.selectOne(ExtAxDocContent::getId, docId); + if (Objects.isNull(content)) { + throw new WorkflowEngineException(MODEL_FILE_CONTENT_DATA_ERROR); + } + return content.getContent(); + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxHiTaskInstServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxHiTaskInstServiceImpl.java index 3af725199..89bfb729c 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxHiTaskInstServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxHiTaskInstServiceImpl.java @@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; @@ -47,7 +48,8 @@ public class ExtAxHiTaskInstServiceImpl implements ExtAxHiTaskInstService { .eq(StringUtils.hasLength(dto.getTaskId()), "task_id", dto.getTaskId()) .eq(Objects.nonNull(dto.getStatus()), "status", Objects.isNull(dto.getStatus()) ? "" : dto.getStatus().getStatus()) - .eq(StringUtils.hasLength(dto.getAssignee()), "assignee", dto.getAssignee()); + .eq(StringUtils.hasLength(dto.getAssignee()), "assignee", dto.getAssignee()) + .notIn(!CollectionUtils.isEmpty(dto.getExcludeIds()), "id", dto.getExcludeIds()); return extAxHiTaskInstMapper.selectList(queryWrapper); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxModelDocServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxModelDocServiceImpl.java new file mode 100644 index 000000000..750eb912f --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxModelDocServiceImpl.java @@ -0,0 +1,392 @@ +package cn.axzo.workflow.core.service.impl; + +import cn.axzo.basics.common.BeanMapper; +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.common.enums.OrderEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCloneDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCreateDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocOrderDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocUpdateDTO; +import cn.axzo.workflow.common.model.response.BpmPageResult; +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import cn.axzo.workflow.core.conf.CustomEventManager; +import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd; +import cn.axzo.workflow.core.engine.event.DocChangeEventImpl; +import cn.axzo.workflow.core.repository.entity.ExtAxDocContent; +import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc; +import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper; +import cn.axzo.workflow.core.service.ExtAxDocContentService; +import cn.axzo.workflow.core.service.ExtAxModelDocService; +import cn.axzo.workflow.core.service.ExtAxReModelService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_NOT_EXISTS; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_TAG_DUPLICATE; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_TAG_EXISTS; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_TYPE_CLONE_NOT_SUPPORT; + +/** + * 模型关联的文档 + * + * @author wangli + * @since 2025-03-28 18:22 + */ +@Service +@Slf4j +public class ExtAxModelDocServiceImpl implements ExtAxModelDocService { + @Resource + private SpringProcessEngineConfiguration springProcessEngineConfiguration; + @Resource + private ExtAxModelDocMapper extAxModelDocMapper; + @Resource + private ExtAxDocContentService extAxDocContentService; + @Resource + private ExtAxReModelService extAxReModelService; + @Resource + private CustomEventManager eventPublisher; + + @Override + public BpmPageResult docPage(DocSearchDTO dto) { + Page page = extAxModelDocMapper.selectPage(new Page<>(dto.getPageNo(), dto.getPageSize()), + buildQueryWrapper(BeanMapper.copyBean(dto, ExtAxModelDoc.class))); + List list = BeanMapper.copyList(page.getRecords(), DocBaseVO.class, + (s, t) -> t.setFileType(FileTypeEnum.valueOfType(s.getFileType()))); + return new BpmPageResult<>(list, page.getTotal()); + } + + @Override + public List docList(DocQueryDTO dto) { + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + return commandExecutor.execute(new CustomGetModelDocsCmd(dto.getProcessInstanceId(), + dto.getProcessDefinitionKey(), dto.getTenantId(), extAxModelDocMapper, extAxReModelService)); + } + + @Override + public List querySetting(String key, String tenantId) { + return extAxModelDocMapper.selectList(new LambdaQueryWrapper() + .eq(StringUtils.hasText(key), ExtAxModelDoc::getModelKey, key) + .eq(Objects.nonNull(tenantId), ExtAxModelDoc::getTenantId, tenantId) + .eq(ExtAxModelDoc::getTempFile, false) + ); + } + + + @Override + public DocBaseVO get(Long docId) { + return get(docId, false); + } + + @Override + public DocBaseVO get(Long docId, boolean ignoreDelete) { + ExtAxModelDoc doc; + if (ignoreDelete) { + doc = extAxModelDocMapper.getIgnoreDelete(docId); + } else { + doc = extAxModelDocMapper.selectById(docId); + if (Objects.isNull(doc)) { + throw new WorkflowEngineException(MODEL_FILE_NOT_EXISTS); + } + } + return BeanMapper.copyBean(doc, DocBaseVO.class, (s, t) -> { + t.setFileType(FileTypeEnum.valueOfType(s.getFileType())); + }); + } + + public static LambdaQueryWrapper buildQueryWrapper(ExtAxModelDoc entity) { + return new LambdaQueryWrapper() + .eq(StringUtils.hasText(entity.getModelId()), ExtAxModelDoc::getModelId, entity.getModelId()) + .eq(StringUtils.hasText(entity.getModelKey()), ExtAxModelDoc::getModelKey, entity.getModelKey()) + .eq(StringUtils.hasText(entity.getFileRelationId()), ExtAxModelDoc::getFileRelationId, + entity.getFileRelationId()) + .eq(StringUtils.hasText(entity.getFileType()), ExtAxModelDoc::getFileType, entity.getFileType()) + .eq(StringUtils.hasText(entity.getTag()), ExtAxModelDoc::getTag, entity.getTag()) + .eq(Objects.nonNull(entity.getStatus()), ExtAxModelDoc::getStatus, entity.getStatus()) + .eq(Objects.nonNull(entity.getTempFile()), ExtAxModelDoc::getTempFile, entity.getTempFile()) + .eq(Objects.isNull(entity.getTempFile()), ExtAxModelDoc::getTempFile, false) + .eq(Objects.nonNull(entity.getRequire()), ExtAxModelDoc::getRequire, entity.getRequire()) + .eq(Objects.nonNull(entity.getTenantId()), ExtAxModelDoc::getTenantId, entity.getTenantId()) + .orderByAsc(ExtAxModelDoc::getOrder) + ; + } + + public Integer maxOrder(DocCreateDTO dto) { + if (Objects.equals(Boolean.TRUE, dto.getTempFile())) { + return 0; + } + ExtAxModelDoc orderQuery = new ExtAxModelDoc(); + orderQuery.setModelId(dto.getModelId()); + List docs = extAxModelDocMapper.selectList(buildQueryWrapper(orderQuery)); + return docs.stream().mapToInt(ExtAxModelDoc::getOrder).max().orElse(0) + 1; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createDoc(DocCreateDTO dto, Boolean fireEvent) { + // 用于发送文档变更事件 + List oldSettings = Collections.emptyList(); + if (fireEvent) { + oldSettings = querySetting(dto.getModelKey(), dto.getTenantId()); + } + + if (StringUtils.hasText(dto.getTag()) && !Objects.equals(Boolean.TRUE, dto.getTempFile())) { + ExtAxModelDoc old = new ExtAxModelDoc(); + old.setModelId(dto.getModelId()); + old.setTag(dto.getTag()); + old.setTenantId(dto.getTenantId()); + Integer count = extAxModelDocMapper.selectCount(buildQueryWrapper(old)); + if (count > 0) { + throw new WorkflowEngineException(MODEL_FILE_TAG_DUPLICATE); + } + } + ExtAxModelDoc entity = BeanMapper.copyBean(dto, ExtAxModelDoc.class); + entity.setFileType(dto.getFileType().name()); + entity.setOrder(maxOrder(dto)); + extAxModelDocMapper.insert(entity); + if (Objects.equals(FileTypeEnum.HIPRINT, dto.getFileType())) { + ExtAxDocContent content = extAxDocContentService.createContent(dto.getContent(), dto.getFileType(), + entity.getId()); + entity.setFileRelationId(String.valueOf(content.getId())); + extAxModelDocMapper.updateById(entity); + } + + if (fireEvent) { + // 发送文档变更事件 + eventPublisher.publishEvent(new DocChangeEventImpl(dto.getModelKey(), dto.getTenantId(), + querySetting(dto.getModelKey(), dto.getTenantId()), oldSettings)); + } + return entity.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateDoc(DocUpdateDTO dto) { + // 用于发送文档变更事件 + List oldSettings = querySetting(dto.getModelKey(), dto.getTenantId()); + + if (StringUtils.hasText(dto.getTag())) { + ExtAxModelDoc query = new ExtAxModelDoc(); + query.setModelId(dto.getModelId()); + query.setTag(dto.getTag()); + query.setTenantId(dto.getTenantId()); + ExtAxModelDoc old = extAxModelDocMapper.selectOne(buildQueryWrapper(query)); + if (Objects.nonNull(old) && !Objects.equals(old.getId(), dto.getId())) { + throw new WorkflowEngineException(MODEL_FILE_TAG_EXISTS); + } + } + ExtAxModelDoc entity = extAxModelDocMapper.selectById(dto.getId()); + if (Objects.isNull(entity)) { + return false; + } + if (Objects.equals(FileTypeEnum.HIPRINT, dto.getFileType())) { + extAxDocContentService.updateContent(dto.getContent(), dto.getFileType(), entity.getFileRelationId()); + } + BeanUtils.copyProperties(dto, entity); + entity.setFileType(dto.getFileType().name()); + int flat = extAxModelDocMapper.updateById(entity); + + // 发送文档变更事件 + eventPublisher.publishEvent(new DocChangeEventImpl(dto.getModelKey(), dto.getTenantId(), + querySetting(dto.getModelKey(), dto.getTenantId()), oldSettings)); + return flat > 0; + } + + /** + * 该功能,只能处理 hiprint 类型 + * + * @param dto + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Long cloneDoc(DocCloneDTO dto) { + return cloneDoc(dto, false); + } + + @Override + public Long cloneDoc(DocCloneDTO dto, Boolean tempFile) { + ExtAxModelDoc origin = extAxModelDocMapper.selectById(dto.getDocId()); + if (Objects.isNull(origin)) { + throw new WorkflowEngineException(MODEL_FILE_NOT_EXISTS); + } + + List oldSettings = new ArrayList<>(); + if (!Objects.equals(Boolean.TRUE, tempFile)) { + // 用于发送文档变更事件 + oldSettings.addAll(querySetting(origin.getModelKey(), origin.getTenantId())); + } + + if (Objects.equals(FileTypeEnum.HIPRINT, FileTypeEnum.valueOfType(origin.getFileType()))) { + DocCreateDTO newDoc = BeanMapper.copyBean(origin, DocCreateDTO.class, (s, t) -> { + t.setFileType(FileTypeEnum.valueOfType(s.getFileType())); + t.setTempFile(Objects.equals(Boolean.TRUE, tempFile)); + if (StringUtils.hasText(dto.getTargetTenantId())) { + // 没设置租户,则使用原来文档的租户 ID + t.setTenantId(dto.getTargetTenantId()); + } + if (StringUtils.hasText(dto.getTargetModelId())) { + // 没设置模型,则使用原来文档的模型 ID + t.setModelId(dto.getTargetModelId()); + } + if (StringUtils.hasText(dto.getTargetFileTag())) { + t.setTag(dto.getTargetFileTag()); + } + }); + newDoc.setContent(extAxDocContentService.getContent(Long.valueOf(origin.getFileRelationId()))); + Long newDocId = createDoc(newDoc, false); + + if (!Objects.equals(Boolean.TRUE, tempFile)) { + // 发送文档变更事件 + eventPublisher.publishEvent(new DocChangeEventImpl(origin.getModelKey(), origin.getTenantId(), + querySetting(origin.getModelKey(), origin.getTenantId()), oldSettings)); + } + return newDocId; + } else { + throw new WorkflowEngineException(MODEL_FILE_TYPE_CLONE_NOT_SUPPORT); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteDoc(Long docId) { + ExtAxModelDoc origin = extAxModelDocMapper.selectById(docId); + if(Objects.isNull(origin)) { + return false; + } + // 用于发送文档变更事件 + List oldSettings = querySetting(origin.getModelKey(), origin.getTenantId()); + + if (Objects.equals(FileTypeEnum.HIPRINT, FileTypeEnum.valueOfType(origin.getFileType()))) { + extAxDocContentService.deleteContent(origin.getFileRelationId()); + } + int flat = extAxModelDocMapper.deleteById(docId); + + // 发送文档变更事件 + eventPublisher.publishEvent(new DocChangeEventImpl(origin.getModelKey(), origin.getTenantId(), + querySetting(origin.getModelKey(), origin.getTenantId()), oldSettings)); + return flat > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean batchDeleteDoc(List docIds) { + if (CollectionUtils.isEmpty(docIds)) { + return true; + } + List entities = extAxModelDocMapper.selectBatchIds(docIds); + if (CollectionUtils.isEmpty(entities)) { + return true; + } + List hiprintList = entities.stream() + .filter(entity -> Objects.equals(FileTypeEnum.HIPRINT, FileTypeEnum.valueOfType(entity.getFileType()))) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(hiprintList)) { + extAxDocContentService.batchDeleteContent(hiprintList.stream() + .map(ExtAxModelDoc::getFileRelationId) + .collect(Collectors.toList())); + } + int flat = extAxModelDocMapper.deleteBatchIds(docIds); + + // 发送文档变更事件 + eventPublisher.publishEvent(new DocChangeEventImpl(entities.get(0).getModelKey(), + entities.get(0).getTenantId(), Collections.emptyList(), entities)); + return flat > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean orderDoc(DocOrderDTO dto) { + ExtAxModelDoc doc = extAxModelDocMapper.selectById(dto.getId()); + if (Objects.isNull(doc)) { + throw new WorkflowEngineException(MODEL_FILE_NOT_EXISTS); + } + + LambdaQueryWrapper orderWrapper = new LambdaQueryWrapper<>(); + orderWrapper.eq(ExtAxModelDoc::getModelId, doc.getModelId()) + .eq(ExtAxModelDoc::getTempFile, false); + Integer currentOrder = doc.getOrder(); + if (Objects.equals(dto.getOrder(), OrderEnum.UP)) { + orderWrapper.eq(ExtAxModelDoc::getOrder, currentOrder - 1); + ExtAxModelDoc upDoc = extAxModelDocMapper.selectOne(orderWrapper); + if (Objects.isNull(upDoc)) { + return false; + } + doc.setOrder(currentOrder - 1); + upDoc.setOrder(currentOrder); + extAxModelDocMapper.updateById(upDoc); + } else { + orderWrapper.eq(ExtAxModelDoc::getOrder, currentOrder + 1); + ExtAxModelDoc downDoc = extAxModelDocMapper.selectOne(orderWrapper); + if (Objects.isNull(downDoc)) { + return false; + } + doc.setOrder(currentOrder + 1); + downDoc.setOrder(currentOrder); + extAxModelDocMapper.updateById(downDoc); + } + extAxModelDocMapper.updateById(doc); + return true; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean statusDoc(DocStatusDTO dto) { + ExtAxModelDoc origin = extAxModelDocMapper.selectById(dto.getId()); + if (Objects.isNull(origin)) { + throw new WorkflowEngineException(MODEL_FILE_NOT_EXISTS); + } + // 用于发送文档变更事件 + List oldSettings = querySetting(origin.getModelKey(), origin.getTenantId()); + + origin.setStatus(Objects.equals(Boolean.TRUE, dto.getStatus())); + int flat = extAxModelDocMapper.updateById(origin); + + // 发送文档变更事件 + eventPublisher.publishEvent(new DocChangeEventImpl(origin.getModelKey(), origin.getTenantId(), + querySetting(origin.getModelKey(), origin.getTenantId()), oldSettings)); + return flat > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean requireDoc(DocStatusDTO dto) { + ExtAxModelDoc origin = extAxModelDocMapper.selectById(dto.getId()); + if (Objects.isNull(origin)) { + throw new WorkflowEngineException(MODEL_FILE_NOT_EXISTS); + } + origin.setRequire(Objects.equals(Boolean.TRUE, dto.getStatus())); + return extAxModelDocMapper.updateById(origin) > 0; + } + + @Override + public List getIds(List ids) { + if (CollectionUtils.isEmpty(ids)) { + return Collections.emptyList(); + } + List docs = extAxModelDocMapper.selectList(new LambdaQueryWrapper() + .in(ExtAxModelDoc::getId, ids) + .eq(ExtAxModelDoc::getTempFile, false) + .eq(ExtAxModelDoc::getIsDelete, 0)); + return BeanMapper.copyList(docs, DocBaseVO.class, + (s, t) -> t.setFileType(FileTypeEnum.valueOfType(s.getFileType()))); + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessLogServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessLogServiceImpl.java index 19a05ef20..7791a9bb7 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessLogServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessLogServiceImpl.java @@ -20,6 +20,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.HIDDEN_ASSIGNEE_ID; import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE; import static cn.axzo.workflow.common.constant.BpmnConstants.ROBOT_ASSIGNEE_ID; +import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.HANDLING; /** * Api Log 表操服务实现 @@ -54,7 +55,7 @@ public class ExtAxProcessLogServiceImpl implements ExtAxProcessLogService { @Override public void updateAssignee(ExtAxProcessLog queryLog, BpmnTaskDelegateAssigner assignee) { - updateAssignee(queryLog, assignee, assignee.getAssignerName()); + updateAssignee(queryLog, assignee, assignee.getAssignerName() + HANDLING.getDesc()); } @@ -66,7 +67,7 @@ public class ExtAxProcessLogServiceImpl implements ExtAxProcessLogService { return; } ExtAxProcessLog update = new ExtAxProcessLog(); - update.setOperationDesc(StringUtils.hasText(operationDesc) ? operationDesc : assignee.getAssignerName()); + update.setOperationDesc(StringUtils.hasText(operationDesc) ? operationDesc : assignee.getAssignerName() + HANDLING.getDesc()); update.setAssigneeFull(Lists.newArrayList(assignee)); update.setAssigneeId(Long.valueOf(assignee.getPersonId())); update.setAssigneeTenantId(assignee.getTenantId()); @@ -80,6 +81,11 @@ public class ExtAxProcessLogServiceImpl implements ExtAxProcessLogService { return extAxProcessLogMapper.selectList(buildQueryWrapper(query)); } + @Override + public ExtAxProcessLog findByProcessIdAndTaskIdWithDeleted(String processId, String taskId) { + return extAxProcessLogMapper.findByProcessIdAndTaskIdWithDeleted(processId, taskId); + } + @Override public void restore(String processInstanceId, String taskId) { batchRestore(Lists.newArrayList(new SimpleTaskDTO(processInstanceId, taskId))); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessSignServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessSignServiceImpl.java new file mode 100644 index 000000000..cc6b79ef1 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxProcessSignServiceImpl.java @@ -0,0 +1,29 @@ +package cn.axzo.workflow.core.service.impl; + +import cn.axzo.workflow.core.repository.entity.ExtAxProcessSign; +import cn.axzo.workflow.core.repository.mapper.ExtAxProcessSignMapper; +import cn.axzo.workflow.core.service.ExtAxProcessSignService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * 流程签署业务信息记录 + * + * @author wangli + * @since 2025-04-02 10:06 + */ +@Service +@Slf4j +public class ExtAxProcessSignServiceImpl extends ServiceImpl implements ExtAxProcessSignService { + + @Resource + private ExtAxProcessSignMapper extAxProcessSignMapper; + + @Override + public ExtAxProcessSign findByProcessInstanceId(String processInstanceId) { + return extAxProcessSignMapper.selectOne(ExtAxProcessSign::getProcessInstanceId, processInstanceId); + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxReModelServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxReModelServiceImpl.java index c3dffe979..fed6ae284 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxReModelServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxReModelServiceImpl.java @@ -1,8 +1,11 @@ package cn.axzo.workflow.core.service.impl; import cn.axzo.basics.common.BeanMapper; -import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.enums.ExtModelStateFieldEnum; import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO; +import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.model.response.print.PrintModelDTO; import cn.axzo.workflow.core.repository.entity.ExtAxReModel; import cn.axzo.workflow.core.repository.mapper.ExtAxReModelMapper; import cn.axzo.workflow.core.service.ExtAxReModelService; @@ -10,12 +13,13 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.List; import java.util.Objects; -import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_ID_NOT_EXISTS; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_NOT_EXISTS; /** @@ -50,12 +54,17 @@ public class ExtAxReModelServiceImpl implements ExtAxReModelService { } @Override - public void changeStatus(String modelId, Integer status) { + public void changeStatus(String modelId, Integer status, ExtModelStateFieldEnum field) { ExtAxReModel reModel = extAxReModelMapper.selectOne(new QueryWrapper().eq("model_id", modelId)); if (Objects.isNull(reModel)) { - throw new WorkflowEngineException(MODEL_ID_NOT_EXISTS, reModel.getModelId()); + throw new WorkflowEngineException(MODEL_NOT_EXISTS); + } + if (Objects.equals(field, ExtModelStateFieldEnum.status)) { + reModel.setStatus(status); + } + if (Objects.equals(field, ExtModelStateFieldEnum.printStatus)) { + reModel.setPrintStatus(status); } - reModel.setStatus(status); extAxReModelMapper.updateById(reModel); } @@ -64,4 +73,41 @@ public class ExtAxReModelServiceImpl implements ExtAxReModelService { ExtAxReModel model = extAxReModelMapper.selectOne(new QueryWrapper().eq("model_id", modelId)); return BeanMapper.copyBean(model, BpmnModelExtVO.class); } + + @Override + public void printTemplateConfig(PrintTemplateConfigUpsertDTO dto) { + ExtAxReModel model = extAxReModelMapper.selectOne(new QueryWrapper().eq("model_id", dto.getModelId())); + if (Objects.isNull(model)) { + throw new WorkflowEngineException(MODEL_NOT_EXISTS); + } + if (Objects.nonNull(dto.getPrintFileName())) { + model.setPrintFileName(dto.getPrintFileName()); + } + if (Objects.nonNull(dto.getPrintTemplateName())) { + model.setPrintTemplateName(dto.getPrintTemplateName()); + } + if (Objects.nonNull(dto.getPrintTemplateConfig())) { + model.setPrintTemplateConfig(dto.getPrintTemplateConfig()); + } + extAxReModelMapper.updateById(model); + } + + @Override + public Boolean hasPrintTemplateConfig(String modelId) { + ExtAxReModel model = extAxReModelMapper.selectOne(new QueryWrapper().eq("model_id", modelId)); + return Objects.equals(1, model.getPrintStatus()) && StringUtils.hasText(model.getPrintTemplateConfig()); + } + + @Override + public PrintModelDTO getPrintTemplateConfig(String modelId) { + ExtAxReModel model = extAxReModelMapper.selectOne(new QueryWrapper().eq("model_id", modelId)); + if (Objects.isNull(model)) { + throw new WorkflowEngineException(MODEL_NOT_EXISTS); + } + return PrintModelDTO.builder() + .printFileName(model.getPrintFileName()) + .printTemplateName(model.getPrintTemplateName()) + .printTemplateConfig(model.getPrintTemplateConfig()) + .build(); + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxReadRecordServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxReadRecordServiceImpl.java new file mode 100644 index 000000000..cdeec873f --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxReadRecordServiceImpl.java @@ -0,0 +1,95 @@ +package cn.axzo.workflow.core.service.impl; + +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO; +import cn.axzo.workflow.core.repository.entity.ExtAxReadRecord; +import cn.axzo.workflow.core.repository.mapper.ExtAxReadRecordMapper; +import cn.axzo.workflow.core.service.ExtAxModelDocService; +import cn.axzo.workflow.core.service.ExtAxReadRecordService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.TaskService; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * 审批人关联文档阅读记录表服务实现 + * + * @author wangli + * @since 2025-04-02 10:06 + */ +@Service +@Slf4j +public class ExtAxReadRecordServiceImpl extends ServiceImpl implements ExtAxReadRecordService { + private static final Object lock = new Object(); + @Resource + private ExtAxReadRecordMapper extAxReadRecordMapper; + @Resource + private TaskService taskService; + @Resource + private ExtAxModelDocService modelDocService; + + @Override + public List queryReadStatus(ApproverReadStatusDTO dto) { + ExtAxReadRecord entity = new ExtAxReadRecord(); + entity.setProcessInstanceId(dto.getProcessInstanceId()); + entity.setPersonId(Long.valueOf(dto.getAssigner().getPersonId())); + ExtAxReadRecord record = extAxReadRecordMapper.selectOne(buildWrapper(entity)); + return Objects.isNull(record) ? Collections.emptyList() : record.getReadStatus(); + } + + @Override + public Boolean changeReadStatus(ChangeApproverReadStatusDTO dto) { + ExtAxReadRecord entity = new ExtAxReadRecord(); + entity.setProcessInstanceId(dto.getProcessInstanceId()); + entity.setPersonId(Long.valueOf(dto.getAssigner().getPersonId())); + + ExtAxReadRecord record; + synchronized (lock) { + record = extAxReadRecordMapper.selectOne(buildWrapper(entity)); + if (Objects.isNull(record)) { + // 新增全新阅读记录 + record = new ExtAxReadRecord(); + record.setProcessInstanceId(dto.getProcessInstanceId()); + record.setPersonId(Long.valueOf(dto.getAssigner().getPersonId())); + record.setReadStatus(Lists.newArrayList(SimpleDocDTO.builder() + .id(dto.getDocId()) + .tag(modelDocService.get(dto.getDocId()).getTag()) + .readStatus(dto.getReadStatus()) + .build())); + extAxReadRecordMapper.insert(record); + return true; + } else { + // 更新现有的阅读记录 + Optional opt = record.getReadStatus().stream().filter(i -> Objects.equals(i.getId(), dto.getDocId())).findFirst(); + if (opt.isPresent()) { + opt.ifPresent(i -> i.setReadStatus(dto.getReadStatus())); + } else { + record.getReadStatus().add(SimpleDocDTO.builder() + .id(dto.getDocId()) + .tag(modelDocService.get(dto.getDocId()).getTag()) + .readStatus(dto.getReadStatus()) + .build()); + } + extAxReadRecordMapper.updateById(record); + return true; + } + } + } + + private LambdaQueryWrapper buildWrapper(ExtAxReadRecord entity) { + return new LambdaQueryWrapper() + .eq(StringUtils.hasText(entity.getProcessInstanceId()), ExtAxReadRecord::getProcessInstanceId, entity.getProcessInstanceId()) + .eq(Objects.nonNull(entity.getPersonId()), ExtAxReadRecord::getPersonId, entity.getPersonId()) + ; + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/FormCoreServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/FormCoreServiceImpl.java index e5dfe0e70..bc7c8b9b1 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/FormCoreServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/FormCoreServiceImpl.java @@ -2,7 +2,6 @@ package cn.axzo.workflow.core.service.impl; import cn.axzo.workflow.common.exception.WorkflowEngineException; import cn.axzo.workflow.common.model.dto.BpmnFormRelationSearchDTO; -import cn.axzo.workflow.common.model.request.form.FormPermissionMetaInfo; import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO; import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; @@ -10,7 +9,6 @@ import cn.axzo.workflow.common.model.response.category.CategoryItemVO; import cn.axzo.workflow.common.model.response.form.FormVO; import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO; import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO; -import cn.axzo.workflow.common.util.ExpressionUtil; import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; import cn.axzo.workflow.core.common.utils.FormHelper; import cn.axzo.workflow.core.engine.cmd.GetFormInstanceAndPermissionCmd; @@ -22,7 +20,6 @@ import cn.axzo.workflow.core.service.ExtAxBpmnFormRelationService; import cn.axzo.workflow.core.service.FormCoreService; import cn.axzo.workflow.form.service.converter.FormFieldConverter; import cn.axzo.workflow.form.service.converter.FormInstanceConverter; -import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.common.engine.api.FlowableObjectNotFoundException; @@ -35,25 +32,19 @@ import org.flowable.form.model.FormField; import org.flowable.form.model.SimpleFormModel; import org.flowable.spring.SpringProcessEngineConfiguration; import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import static cn.axzo.workflow.common.code.FormModelRespCode.FORM_MODEL_NOT_EXISTS; import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; -import static cn.axzo.workflow.common.constant.FormConstants.FIELD_PROPERTY_DEFAULT_VALUE; -import static cn.axzo.workflow.common.constant.FormConstants.FIELD_PROPERTY_EDITABLE; -import static cn.axzo.workflow.common.constant.FormConstants.FIELD_PROPERTY_HIDDEN; -import static cn.axzo.workflow.common.constant.FormConstants.FIELD_PROPERTY_READONLY; -import static cn.axzo.workflow.common.constant.FormConstants.FIELD_PROPERTY_REQUIRED; import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; /** @@ -103,29 +94,33 @@ public class FormCoreServiceImpl implements FormCoreService { public FormDefinitionVO getStartForm(StartFormSearchDTO dto) { FormInfo formModel; try { - formModel = formRepositoryService.getFormModelByKey(dto.getKey(), dto.getTenantId(), true); + BpmnProcessDefinitionVO definitionVO = bpmnProcessDefinitionService.getActiveProcessDefinitionByKey(dto.getKey(), dto.getTenantId()); + formModel = formRepositoryService.getFormModelByKey(dto.getKey(), definitionVO.getTenantId(), false); + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + BpmnModel bpmnModel = commandExecutor.execute(new GetBpmnModelCmd(definitionVO.getId())); + BpmnMetaParserHelper.getFormFieldPermissionConf(bpmnModel.getFlowElement(NODE_STARTER.getType())) + .ifPresent(permission -> { + if (formModel.getFormModel() instanceof SimpleFormModel) { + SimpleFormModel simpleFormModel = (SimpleFormModel) formModel.getFormModel(); + FormHelper.populateFormModel(simpleFormModel, permission, new HashMap<>(), dto.getShowOriginDefaultValue()); + } + }); + FormDefinitionVO formDefinitionVO = new FormDefinitionVO(); + formDefinitionVO.setFormDefinitionId(formModel.getId()); + formDefinitionVO.setName(formModel.getName()); + formDefinitionVO.setKey(formModel.getKey()); + formDefinitionVO.setVersion(formModel.getVersion()); + formDefinitionVO.setTenantId(dto.getTenantId()); + List fields = ((SimpleFormModel) formModel.getFormModel()).getFields(); + formDefinitionVO.setFields(formFieldConverter.toVos(fields)); + return formDefinitionVO; } catch (FlowableObjectNotFoundException e) { log.warn("can't found form model"); - throw new WorkflowEngineException(FORM_MODEL_NOT_EXISTS); - } - BpmnProcessDefinitionVO definitionVO = bpmnProcessDefinitionService.getActiveProcessDefinitionByKey(dto.getKey(), dto.getTenantId()); - CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); - BpmnModel bpmnModel = commandExecutor.execute(new GetBpmnModelCmd(definitionVO.getId())); - BpmnMetaParserHelper.getFormFieldPermissionConf(bpmnModel.getFlowElement(NODE_STARTER.getType())).ifPresent(permission -> { - if (formModel.getFormModel() instanceof SimpleFormModel) { - SimpleFormModel simpleFormModel = (SimpleFormModel) formModel.getFormModel(); - FormHelper.populateFormModel(simpleFormModel, permission, new HashMap<>(), dto.getShowOriginDefaultValue()); + if (Objects.equals(Boolean.TRUE, dto.getThrowException())) { + throw new WorkflowEngineException(FORM_MODEL_NOT_EXISTS); } - }); - FormDefinitionVO formDefinitionVO = new FormDefinitionVO(); - formDefinitionVO.setFormDefinitionId(formModel.getId()); - formDefinitionVO.setName(formModel.getName()); - formDefinitionVO.setKey(formModel.getKey()); - formDefinitionVO.setVersion(formModel.getVersion()); - formDefinitionVO.setTenantId(dto.getTenantId()); - List fields = ((SimpleFormModel) formModel.getFormModel()).getFields(); - formDefinitionVO.setFields(formFieldConverter.toVos(fields)); - return formDefinitionVO; + } + return null; } @Override diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java index 09f4134e0..c1c3d6535 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/FlowNodeForecastService.java @@ -134,8 +134,8 @@ public class FlowNodeForecastService implements InitializingBean { throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS); } else { instance = runtimeService.createProcessInstanceQuery() - .processInstanceId(processInstanceId) - .singleResult(); + .processInstanceId(processInstanceId) + .singleResult(); } if (Objects.isNull(instance)) { throw new WorkflowEngineException(RUNNING_INSTANCE_ONLY_FORECAST); @@ -144,9 +144,27 @@ public class FlowNodeForecastService implements InitializingBean { // 保持推测出来的节点执行顺序的容器 List orderedNodes = new ArrayList<>(); + + doForecasting(processInstanceId, taskDefinitionKey, containSelf, orderedNodes, bpmnModel); + return orderedNodes; + } + + /** + * 根据 BpmnModel 推测节点运行顺序 + * + * @param processInstanceId 如果有值,则用该实例内的变量进行推测 + * @param taskDefinitionKey 如果有值,则从 BpmnModel 中的该节点往下推测 + * @param containSelf 配合第二个参数,是否包含第二个参数的节点 + * @param orderedNodes 结果集 + * @param bpmnModel 模型 + */ + public void doForecasting(String processInstanceId, + String taskDefinitionKey, + Boolean containSelf, + List orderedNodes, + BpmnModel bpmnModel) { // 流程定义中所有的FlowElement Collection flowElements = bpmnModel.getMainProcess().getFlowElements(); - if (StringUtils.hasText(taskDefinitionKey)) { if (Boolean.TRUE.equals(containSelf)) { flowElements.stream().filter(e -> Objects.equals(e.getId(), taskDefinitionKey)).findFirst().ifPresent(orderedNodes::add); @@ -170,7 +188,6 @@ public class FlowNodeForecastService implements InitializingBean { } startForecasting(orderedNodes, processInstanceId); - return orderedNodes; } private void startForecasting(List orderNodes, String processInstanceId) { @@ -195,8 +212,8 @@ public class FlowNodeForecastService implements InitializingBean { */ private Optional findStartNode(Collection flowElements) { return Optional.ofNullable(flowElements.stream().filter(StartEvent.class::isInstance).findFirst() - .map(StartEvent.class::cast) - .orElseThrow(() -> new IllegalArgumentException("非法的参数, 正确的流程定义一定有一个 StartEvent 节点"))); + .map(StartEvent.class::cast) + .orElseThrow(() -> new IllegalArgumentException("非法的参数, 正确的流程定义一定有一个 StartEvent 节点"))); } @@ -217,7 +234,7 @@ public class FlowNodeForecastService implements InitializingBean { public void afterPropertiesSet() { forecasts.forEach(i -> { Class rawClass = ResolvableType.forClass(i.getClass(), Forecast.class) - .getSuperType().getGenerics()[0].getRawClass(); + .getSuperType().getGenerics()[0].getRawClass(); FORECAST_MAP.put(rawClass, (AbstractForecast) i); }); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/forecast/impl/SequenceFlowForecasting.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/forecast/impl/SequenceFlowForecasting.java index b6ee64635..c74b4b21d 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/forecast/impl/SequenceFlowForecasting.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/support/forecast/impl/SequenceFlowForecasting.java @@ -42,12 +42,15 @@ public class SequenceFlowForecasting extends AbstractForecast { return Collections.emptyList(); } - // 评估顺序流集合中条件为 true 的顺序流 - List executableFlows = - flowElements.stream().filter(i -> StringUtils.hasLength(i.getConditionExpression())) - .filter(i -> conditionOn(i.getConditionExpression(), processInstanceId)).collect(Collectors.toList()); - if (!CollectionUtils.isEmpty(executableFlows) && executableFlows.size() == 1) { - return Lists.newArrayList(executableFlows.get(0)); + // 如果流程实例 ID 为空,则全走默认分支 + if (StringUtils.hasText(processInstanceId)) { + // 评估顺序流集合中条件为 true 的顺序流 + List executableFlows = + flowElements.stream().filter(i -> StringUtils.hasLength(i.getConditionExpression())) + .filter(i -> conditionOn(i.getConditionExpression(), processInstanceId)).collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(executableFlows) && executableFlows.size() == 1) { + return Lists.newArrayList(executableFlows.get(0)); + } } List defaultFlows = new ArrayList<>(); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/util/DingTalkUtils.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/util/DingTalkUtils.java index 55f0c5e92..b95644ab3 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/util/DingTalkUtils.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/util/DingTalkUtils.java @@ -1,6 +1,7 @@ package cn.axzo.workflow.core.util; import cn.axzo.workflow.common.model.dto.AlterDTO; +import cn.hutool.core.date.DateUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.dingtalk.api.DefaultDingTalkClient; @@ -10,7 +11,6 @@ import com.dingtalk.api.response.OapiRobotSendResponse; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; -import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.util.HashMap; @@ -74,6 +74,7 @@ public class DingTalkUtils { OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown(); markdown.setTitle("Notice " + title + ", Env: " + profile); String text = "#### [" + profile + "]" + title + "\n" + + "> 时间: " + DateUtil.now() + "\n" + "> 入参: " + JSONUtil.toJsonStr(req) + "\n\n" + "> ###### 异常信息: " + JSONUtil.toJsonStr(Objects.isNull(throwable.getCause()) ? throwable.getMessage() : throwable.getCause().getMessage()) + " \n\n" + @@ -134,9 +135,9 @@ public class DingTalkUtils { } public static void sendDingTalkForBizNodeAlter(String profile, AlterDTO alterDTO, List atMobiles) { - if (CollectionUtils.isEmpty(atMobiles)) { - return; - } +// if (CollectionUtils.isEmpty(atMobiles)) { +// return; +// } String processInstanceId = alterDTO.getProcessInstanceId(); OapiRobotSendRequest request = new OapiRobotSendRequest(); request.setMsgtype("markdown"); @@ -145,16 +146,28 @@ public class DingTalkUtils { markdown.setText("#### [" + profile + "]业务节点长时间停止\n" + "> 节点相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" + "> ##### [点击查看审批日志](" + getWebUrl(profile) + "/#/workflow/examples?processInstanceId=" + processInstanceId + ") \n\n" + - "> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询"); + "> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询 \n\n" + + mobiles(atMobiles)); request.setMarkdown(markdown); OapiRobotSendRequest.At at = new OapiRobotSendRequest.At(); at.setAtMobiles(atMobiles); at.setIsAtAll(false); request.setAt(at); -// sendDingTalk(request); + sendDingTalk(request); } - public static void sendDingTalkForTransferToAdminError(String profile, String processInstanceId, String taskDefinitionKey, Object orgScopes) { + public static String mobiles(List atMobiles) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < atMobiles.size(); i++) { + if (i != 0) { + sb.append(","); + } + sb.append("@").append(atMobiles.get(i)); + } + return sb.toString(); + } + + public static void sendDingTalkForTransferToAdminError(String profile, String processInstanceId, String taskDefinitionKey, Object orgScopes, String targetUrl) { OapiRobotSendRequest request = new OapiRobotSendRequest(); request.setMsgtype("markdown"); OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown(); @@ -163,6 +176,7 @@ public class DingTalkUtils { "> 流程实例 ID: " + processInstanceId + "\n\n" + "> 节点参数(节点 ID): " + taskDefinitionKey + "\n\n" + "> OrgScopes 参数信息: " + JSONUtil.toJsonStr(orgScopes) + "\n\n" + + "> 目标接口: " + targetUrl + "\n\n" + "> ##### 提示:仅作为提示信息,不代表是异常"); request.setMarkdown(markdown); sendDingTalk(request); diff --git a/workflow-engine-core/src/main/java/org/flowable/engine/impl/cmd/CreateAttachmentCmd.java b/workflow-engine-core/src/main/java/org/flowable/engine/impl/cmd/CreateAttachmentCmd.java index 9f0f63f53..debe1e8ff 100644 --- a/workflow-engine-core/src/main/java/org/flowable/engine/impl/cmd/CreateAttachmentCmd.java +++ b/workflow-engine-core/src/main/java/org/flowable/engine/impl/cmd/CreateAttachmentCmd.java @@ -2,13 +2,11 @@ package org.flowable.engine.impl.cmd; import cn.axzo.workflow.core.engine.cmd.AbstractCommand; import com.alibaba.fastjson.JSON; -import liquibase.pro.packaged.M; import org.flowable.common.engine.api.FlowableException; import org.flowable.common.engine.api.FlowableObjectNotFoundException; import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; import org.flowable.common.engine.impl.identity.Authentication; -import org.flowable.common.engine.impl.interceptor.Command; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntity; import org.flowable.common.engine.impl.util.IoUtil; @@ -37,6 +35,7 @@ import java.util.Map; */ public class CreateAttachmentCmd extends AbstractCommand implements Serializable { + private static final long serialVersionUID = -2901013696126749407L; protected String attachmentType; protected String taskId; protected String processInstanceId; diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/conf/FormFlowableConfiguration.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/conf/FormFlowableConfiguration.java index 6265b9c24..5e2623d4c 100644 --- a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/conf/FormFlowableConfiguration.java +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/conf/FormFlowableConfiguration.java @@ -1,5 +1,6 @@ package cn.axzo.workflow.form.conf; +import cn.axzo.workflow.form.engine.api.CustomFormServiceImpl; import org.flowable.form.spring.SpringFormEngineConfiguration; import org.flowable.spring.boot.EngineConfigurationConfigurer; import org.springframework.context.annotation.Bean; @@ -18,6 +19,9 @@ public class FormFlowableConfiguration { @Bean public EngineConfigurationConfigurer formEngineConfigurer() { - return configuration -> configuration.setDatabaseSchemaUpdate(DB_SCHEMA_UPDATE_TRUE); + return configuration -> { + configuration.setDatabaseSchemaUpdate(DB_SCHEMA_UPDATE_TRUE); + configuration.setFormService(new CustomFormServiceImpl(configuration)); + }; } } diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/api/CustomFormServiceImpl.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/api/CustomFormServiceImpl.java new file mode 100644 index 000000000..ee3f8bb1a --- /dev/null +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/api/CustomFormServiceImpl.java @@ -0,0 +1,43 @@ +package cn.axzo.workflow.form.engine.api; + +import cn.axzo.workflow.form.engine.cmd.CustomCreateFormInstanceCmd; +import cn.axzo.workflow.form.engine.cmd.CustomGetVariablesFromFormSubmissionCmd; +import org.flowable.form.api.FormInfo; +import org.flowable.form.api.FormInstance; +import org.flowable.form.engine.FormEngineConfiguration; +import org.flowable.form.engine.impl.FormServiceImpl; + +import java.util.Map; + +/** + * 自定义扩展表单引擎中的 FormService + * + * @author wangli + * @since 2025-01-22 11:19 + */ +public class CustomFormServiceImpl extends FormServiceImpl { + public CustomFormServiceImpl(FormEngineConfiguration engineConfiguration) { + super(engineConfiguration); + } + + @Override + public void validateFormFields(FormInfo formInfo, Map values) { + // StartEvent Form Fields Validation + super.validateFormFields(formInfo, values); + } + + @Override + public FormInstance createFormInstance(Map variables, FormInfo formInfo, String taskId, String processInstanceId, String processDefinitionId, String tenantId, String outcome) { + return commandExecutor.execute(new CustomCreateFormInstanceCmd(formInfo, variables, taskId, processInstanceId, processDefinitionId, tenantId, outcome)); + } + + @Override + public Map getVariablesFromFormSubmission(FormInfo formInfo, Map values) { + return commandExecutor.execute(new CustomGetVariablesFromFormSubmissionCmd(formInfo, values)); + } + + @Override + public Map getVariablesFromFormSubmission(FormInfo formInfo, Map values, String outcome) { + return commandExecutor.execute(new CustomGetVariablesFromFormSubmissionCmd(formInfo, values, outcome)); + } +} diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/cmd/CustomCreateFormInstanceCmd.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/cmd/CustomCreateFormInstanceCmd.java new file mode 100644 index 000000000..d66daee7c --- /dev/null +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/cmd/CustomCreateFormInstanceCmd.java @@ -0,0 +1,168 @@ +package cn.axzo.workflow.form.engine.cmd; + +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.form.api.FormInfo; +import org.flowable.form.api.FormInstance; +import org.flowable.form.engine.FormEngineConfiguration; +import org.flowable.form.engine.impl.cmd.CreateFormInstanceCmd; +import org.flowable.form.engine.impl.persistence.entity.FormInstanceEntity; +import org.flowable.form.engine.impl.persistence.entity.FormInstanceEntityManager; +import org.flowable.form.engine.impl.util.CommandContextUtil; +import org.flowable.form.model.FormField; +import org.flowable.form.model.FormFieldTypes; +import org.flowable.form.model.SimpleFormModel; +import org.joda.time.LocalDate; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_DATA_PARSE_ERROR_BY_AMOUNT; +import static cn.axzo.workflow.common.code.FormInstanceRespCode.FORM_DATA_PARSE_ERROR_BY_UPLOAD; + +/** + * 覆盖表单引擎的创建表单实例的命令 + * + * @author wangli + * @since 2025-01-22 16:52 + */ +public class CustomCreateFormInstanceCmd extends CreateFormInstanceCmd { + public CustomCreateFormInstanceCmd(FormInfo formInfo, Map variables, String taskId, String processInstanceId, String processDefinitionId, String tenantId, String outcome) { + super(formInfo, variables, taskId, processInstanceId, processDefinitionId, tenantId, outcome); + } + + public CustomCreateFormInstanceCmd(String formModelId, Map variables, String taskId, String processInstanceId, String processDefinitionId, String tenantId, String outcome) { + super(formModelId, variables, taskId, processInstanceId, processDefinitionId, tenantId, outcome); + } + + public CustomCreateFormInstanceCmd(FormInfo formInfo, Map variables, String taskId, String scopeId, String scopeType, String scopeDefinitionId, String tenantId, String outcome) { + super(formInfo, variables, taskId, scopeId, scopeType, scopeDefinitionId, tenantId, outcome); + } + + public CustomCreateFormInstanceCmd(String formModelId, Map variables, String taskId, String scopeId, String scopeType, String scopeDefinitionId, String tenantId, String outcome) { + super(formModelId, variables, taskId, scopeId, scopeType, scopeDefinitionId, tenantId, outcome); + } + + @Override + public FormInstance execute(CommandContext commandContext) { + FormEngineConfiguration formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(); + if (formInfo == null) { + if (formModelId == null) { + throw new FlowableException("Invalid form model and no form model Id provided"); + } + formInfo = CommandContextUtil.getFormEngineConfiguration().getFormRepositoryService().getFormModelById(formModelId); + } + + if (formInfo == null || formInfo.getId() == null) { + throw new FlowableException("Invalid form model provided"); + } + + ObjectMapper objectMapper = formEngineConfiguration.getObjectMapper(); + ObjectNode submittedFormValuesJson = objectMapper.createObjectNode(); + + ObjectNode valuesNode = submittedFormValuesJson.putObject("values"); + + // Loop over all form fields and see if a value was provided + + SimpleFormModel formModel = (SimpleFormModel) formInfo.getFormModel(); + Map fieldMap = formModel.allFieldsAsMap(); + for (String fieldId : fieldMap.keySet()) { + FormField formField = fieldMap.get(fieldId); + + if (FormFieldTypes.EXPRESSION.equals(formField.getType()) || FormFieldTypes.CONTAINER.equals(formField.getType())) { + continue; + } + + if (variables != null && variables.containsKey(fieldId)) { + Object variableValue = variables.get(fieldId); + if (variableValue == null) { + valuesNode.putNull(fieldId); + } else if (variableValue instanceof Long) { + valuesNode.put(fieldId, (Long) variables.get(fieldId)); + + } else if (variableValue instanceof Double) { + valuesNode.put(fieldId, (Double) variables.get(fieldId)); + + } else if (variableValue instanceof Boolean) { + valuesNode.put(fieldId, (Boolean) variables.get(fieldId)); + + } else if (variableValue instanceof LocalDate) { + valuesNode.put(fieldId, ((LocalDate) variableValue).toString()); + } else if (variableValue instanceof Collection) { + if (CollectionUtils.isEmpty((Collection) variableValue)) { + variableValue = Collections.emptyList(); + } + try { + valuesNode.put(fieldId, objectMapper.writeValueAsString(variableValue)); + } catch (JsonProcessingException e) { + throw new WorkflowEngineException(FORM_DATA_PARSE_ERROR_BY_UPLOAD); + } + } else if (variableValue instanceof Map) { + if (CollectionUtils.isEmpty((Map) variableValue)) { + variableValue = Collections.emptyMap(); + } + try { + valuesNode.put(fieldId, objectMapper.writeValueAsString(variableValue)); + } catch (JsonProcessingException e) { + throw new WorkflowEngineException(FORM_DATA_PARSE_ERROR_BY_AMOUNT); + } + } else { + valuesNode.put(fieldId, variableValue.toString()); + } + } + } + + if (outcome != null) { + submittedFormValuesJson.put("flowable_form_outcome", outcome); + } + + FormInstanceEntityManager formInstanceEntityManager = CommandContextUtil.getFormInstanceEntityManager(commandContext); + FormInstanceEntity formInstanceEntity = findExistingFormInstance(formEngineConfiguration); + + if (formInstanceEntity == null) { + formInstanceEntity = formInstanceEntityManager.create(); + } + + formInstanceEntity.setFormDefinitionId(formInfo.getId()); + formInstanceEntity.setTaskId(taskId); + + if (processInstanceId != null) { + formInstanceEntity.setProcessInstanceId(processInstanceId); + formInstanceEntity.setProcessDefinitionId(processDefinitionId); + + } else { + formInstanceEntity.setScopeId(scopeId); + formInstanceEntity.setScopeType(scopeType); + formInstanceEntity.setScopeDefinitionId(scopeDefinitionId); + } + + formInstanceEntity.setSubmittedDate(formEngineConfiguration.getClock().getCurrentTime()); + formInstanceEntity.setSubmittedBy(Authentication.getAuthenticatedUserId()); + + if (tenantId != null) { + formInstanceEntity.setTenantId(tenantId); + } + + try { + formInstanceEntity.setFormValueBytes(objectMapper.writeValueAsBytes(submittedFormValuesJson)); + } catch (Exception e) { + throw new FlowableException("Error setting form values JSON", e); + } + + if (formInstanceEntity.getId() == null) { + formInstanceEntityManager.insert(formInstanceEntity); + } else { + formInstanceEntityManager.update(formInstanceEntity); + } + + + return formInstanceEntity; + } +} diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/cmd/CustomGetVariablesFromFormSubmissionCmd.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/cmd/CustomGetVariablesFromFormSubmissionCmd.java new file mode 100644 index 000000000..11f35e163 --- /dev/null +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/cmd/CustomGetVariablesFromFormSubmissionCmd.java @@ -0,0 +1,143 @@ +package cn.axzo.workflow.form.engine.cmd; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.form.api.FormInfo; +import org.flowable.form.engine.FormEngineConfiguration; +import org.flowable.form.engine.impl.cmd.GetVariablesFromFormSubmissionCmd; +import org.flowable.form.engine.impl.util.CommandContextUtil; +import org.flowable.form.model.FormField; +import org.flowable.form.model.FormFieldTypes; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Date; +import java.util.Map; + +/** + * 覆盖表单引擎自带的表单提交处理命令 + * + * @author wangli + * @since 2025-01-22 15:20 + */ +public class CustomGetVariablesFromFormSubmissionCmd extends GetVariablesFromFormSubmissionCmd { + + public CustomGetVariablesFromFormSubmissionCmd(FormInfo formInfo, Map values) { + super(formInfo, values); + } + + public CustomGetVariablesFromFormSubmissionCmd(FormInfo formInfo, Map values, String outcome) { + super(formInfo, values, outcome); + } + + @Override + public Map execute(CommandContext commandContext) { + return super.execute(commandContext); + } + + @Override + protected Object transformFormFieldValueToVariableValue(FormField formField, Object formFieldValue) { + Object result = formFieldValue; + if (formField.getType().equals(FormFieldTypes.DATE) && formFieldValue instanceof String) { + if (StringUtils.isNotEmpty((String) formFieldValue)) { + try { +// result = LocalDate.parse((String) formFieldValue); + result = parseToLocalDateTime((String) formFieldValue, fmtConvert(formField.getParam("fmt").toString())); + } catch (Exception e) { + e.printStackTrace(); + result = null; + } + } + + } else if (formField.getType().equals(FormFieldTypes.DATE) && formFieldValue instanceof Date) { + result = parseToLocalDateTime((String) formFieldValue, fmtConvert(formField.getParam("fmt").toString())); +// result = new LocalDate(formFieldValue); + } else if (formField.getType().equals(FormFieldTypes.INTEGER) && formFieldValue instanceof String) { + String strFieldValue = (String) formFieldValue; + if (StringUtils.isNotEmpty(strFieldValue) && NumberUtils.isCreatable(strFieldValue)) { + result = Long.valueOf(strFieldValue); + + } else { + result = null; + } + + } else if (formField.getType().equals(FormFieldTypes.DECIMAL) && formFieldValue instanceof String) { + String strFieldValue = (String) formFieldValue; + if (StringUtils.isNotEmpty(strFieldValue) && NumberUtils.isCreatable(strFieldValue)) { + result = Double.valueOf(strFieldValue); + + } else { + result = null; + } + + } else if (formField.getType().equals(FormFieldTypes.AMOUNT) && formFieldValue instanceof String) { + try { + result = Double.parseDouble((String) formFieldValue); + + } catch (NumberFormatException e) { + result = null; + } + + } else if (formField.getType().equals(FormFieldTypes.DROPDOWN) || formField.getType().equals(FormFieldTypes.RADIO_BUTTONS)) { + if (formFieldValue instanceof Map) { + result = ((Map) formFieldValue).get("id"); + if (result == null) { + // fallback to name for manual config options + result = ((Map) formFieldValue).get("name"); + } + } + + } else if (formField.getType().equals(FormFieldTypes.UPLOAD)) { + FormEngineConfiguration formEngineConfiguration = CommandContextUtil.getFormEngineConfiguration(); + try { + result = formEngineConfiguration.getObjectMapper().writeValueAsString(formFieldValue); + } catch (JsonProcessingException e) { + result = null; + } + + } else if (formField.getType().equals(FormFieldTypes.PEOPLE) || formField.getType().equals(FormFieldTypes.FUNCTIONAL_GROUP)) { + if (formFieldValue instanceof Map) { + Map value = (Map) formFieldValue; + result = value.get("id").toString(); + + } else { + // Incorrect or empty map, ignore + result = null; + } + } + + // Default: no processing needs to be done, can be stored as-is + return result; + } + + + public static LocalDateTime parseToLocalDateTime(String dateStr, String format) { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format.replace("YYYY", "yyyy").replace("DD", "dd")); + if (format.contains("HH:mm")) { + return LocalDateTime.parse(dateStr, formatter); + } else { + LocalDate localDate = LocalDate.parse(dateStr, formatter); + return localDate.atStartOfDay(); + } + } catch (DateTimeParseException e) { + System.err.println("解析日期字符串时出错: " + dateStr + " ,使用格式: " + format); + return null; + } + } + + public static String fmtConvert(String fmt) { + switch (fmt) { + case "YYYY-MM-DD HH:mm:ss": + return "yyyy-MM-dd HH:mm:ss"; + case "YYYY-MM-DD HH:mm": + return "yyyy-MM-dd HH:mm"; + default: + return "yyyy-MM-dd"; + } + } +} diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/model/CustomSimpleFormModel.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/model/CustomSimpleFormModel.java new file mode 100644 index 000000000..6e5052e56 --- /dev/null +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/engine/model/CustomSimpleFormModel.java @@ -0,0 +1,47 @@ +package cn.axzo.workflow.form.engine.model; + +import org.flowable.form.model.FormContainer; +import org.flowable.form.model.FormField; +import org.flowable.form.model.SimpleFormModel; + +import java.util.List; + +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_CUSTOM_COMPONENT; + +/** + * @author wangli + * @since 2025-01-24 15:49 + */ +public class CustomSimpleFormModel extends SimpleFormModel { + public CustomSimpleFormModel(SimpleFormModel simpleFormModel) { + this.setName(simpleFormModel.getName()); + this.setKey(simpleFormModel.getKey()); + this.setVersion(simpleFormModel.getVersion()); + this.setDescription(simpleFormModel.getDescription()); + this.setFields(simpleFormModel.getFields()); + this.setOutcomes(simpleFormModel.getOutcomes()); + this.setOutcomeVariableName(simpleFormModel.getOutcomeVariableName()); + } + + private static final long serialVersionUID = 1L; + + @Override + protected void collectSubFields(List fields, List listOfAllFields) { + if (fields != null && fields.size() > 0) { + for (FormField field : fields) { + listOfAllFields.add(field); + if (field instanceof FormContainer && !FORM_FIELD_TYPE_CUSTOM_COMPONENT.equals(field.getType())) { + FormContainer container = (FormContainer) field; + List> subFields = container.getFields(); + if (subFields != null) { + for (List subFieldDefinitions : subFields) { + if (subFieldDefinitions != null) { + collectSubFields(subFieldDefinitions, listOfAllFields); + } + } + } + } + } + } + } +} diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/FormInstanceService.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/FormInstanceService.java index 5b9164182..de4745766 100644 --- a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/FormInstanceService.java +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/FormInstanceService.java @@ -4,7 +4,6 @@ package cn.axzo.workflow.form.service; import cn.axzo.workflow.common.model.request.form.instance.FormContentUpdateDTO; import cn.axzo.workflow.common.model.request.form.instance.FormInstanceSearchDTO; import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO; -import org.flowable.form.api.FormInstanceInfo; /** * 表单实例相关接口 diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/ConversionUtils.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/ConversionUtils.java index 6e478eec7..e6cf5c63c 100644 --- a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/ConversionUtils.java +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/ConversionUtils.java @@ -6,25 +6,28 @@ package cn.axzo.workflow.form.service.converter; * @author wangli * @since 2024-11-11 13:42 */ -import java.util.ArrayList; + +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + import java.util.List; public class ConversionUtils { - public static List convertObjectToList(Object obj) { + public static Object convertObject(Object obj) { if (obj instanceof List) { List list = (List) obj; - List stringList = new ArrayList<>(); - for (Object item : list) { - if (item instanceof String) { - stringList.add((String) item); - } else { - // 处理非字符串类型的情况,可以选择忽略、抛出异常或进行其他处理 - } + if (CollectionUtils.isEmpty(list)) { + return null; + } + return list; + } else if (obj instanceof String) { + if (StringUtils.pathEquals(obj.toString(), "[]")) { + return null; } - return stringList; - } else { // 如果输入不是列表类型,返回空列表或进行其他处理 - return new ArrayList<>(); + return obj; + } else { + return obj; } } } diff --git a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/FormFieldConverter.java b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/FormFieldConverter.java index 09137620d..e9d46e2fa 100644 --- a/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/FormFieldConverter.java +++ b/workflow-engine-form/src/main/java/cn/axzo/workflow/form/service/converter/FormFieldConverter.java @@ -29,7 +29,7 @@ public interface FormFieldConverter extends EntityConverter castList(FormField field) { - List values = new ArrayList<>(); - Object value = field.getValue(); - if (Objects.isNull(value)) { - return values; - } - if (Objects.equals(field.getType(), FormFieldTypes.UPLOAD)) { - return ((Collection) field.getValue()).stream() - .filter(StringUtils::hasText) - .map(i -> UploadFieldDTO.toObject(i).toString()) - .collect(Collectors.toList()); - } - if (value instanceof Collection) { - return (List) value; - } - if (value instanceof String) { - values.add((String) value); - } - return values; - } // 辅助方法,用于处理List到List的转换,调用上面自定义的转换方法 default List formContainersToFormFieldDTOs(List formContainers) { diff --git a/workflow-engine-server/pom.xml b/workflow-engine-server/pom.xml index cd319b356..10cae3ff8 100644 --- a/workflow-engine-server/pom.xml +++ b/workflow-engine-server/pom.xml @@ -67,6 +67,10 @@ ${project.groupId} workflow-engine-core + + ${project.groupId} + workflow-engine-form + ${project.groupId} workflow-engine-elasticsearch @@ -146,6 +150,10 @@ cn.axzo.org org-api + + cn.axzo.nanopart + doc-api + diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/WorkflowEngineApplication.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/WorkflowEngineApplication.java index 24b5cfc84..31e42d3b1 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/WorkflowEngineApplication.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/WorkflowEngineApplication.java @@ -13,7 +13,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; @MapperScan({"cn.axzo.workflow.core.**.mapper", "cn.axzo.workflow.admin.**.mapper"}) @ComponentScan({"cn.axzo.workflow"}) -@EnableFeignClients("cn.axzo.oss") +@EnableFeignClients({"cn.axzo.oss", "cn.axzo.riven.client.feign", "cn.axzo.msg.center", "cn.axzo.basics.profiles"}) @SpringBootApplication(exclude = RabbitAutoConfiguration.class) @EnableTransactionManagement @EnableCaching diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/advice/FlowableOptimisticLockingExceptionHandlerAdvice.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/advice/FlowableOptimisticLockingExceptionHandlerAdvice.java index 841a2d04e..6f97e8c35 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/advice/FlowableOptimisticLockingExceptionHandlerAdvice.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/advice/FlowableOptimisticLockingExceptionHandlerAdvice.java @@ -9,8 +9,6 @@ import org.flowable.common.engine.api.FlowableOptimisticLockingException; import org.flowable.common.engine.impl.interceptor.CommandContext; import org.springframework.stereotype.Component; -import static cn.axzo.framework.domain.web.code.BaseCode.UNAVAILABLE_FOR_LEGAL_REASONS; - /** * 降级 FlowableOptimisticLockingException 异常,该异常在 Flowable 框架中是可以忽略的 * diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/alter/DingTalkAlter.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/alter/DingTalkAlter.java index 9a6fd7516..3a268e62f 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/alter/DingTalkAlter.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/alter/DingTalkAlter.java @@ -1,13 +1,25 @@ package cn.axzo.workflow.server.alter; +import cn.axzo.framework.jackson.utility.JSON; +import cn.axzo.riven.client.common.enums.DingTalkMsgTypeEnum; +import cn.axzo.riven.client.feign.DingDingMsgApi; +import cn.axzo.riven.client.model.SampleMarkdown; +import cn.axzo.riven.client.req.DingDingSendRebootGroupMsgReq; import cn.axzo.workflow.common.model.dto.AlterDTO; import cn.axzo.workflow.core.conf.SupportRefreshProperties; import cn.axzo.workflow.core.listener.Alter; import cn.axzo.workflow.core.util.DingTalkUtils; +import cn.hutool.json.JSONUtil; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.Resource; +import java.util.Objects; + +import static cn.axzo.workflow.core.util.DingTalkUtils.getWebUrl; +import static cn.axzo.workflow.core.util.DingTalkUtils.mobiles; /** * 钉钉告警实现 @@ -15,6 +27,7 @@ import javax.annotation.Resource; * @author wangli * @since 2024-09-13 11:40 */ +@Slf4j @Component public class DingTalkAlter implements Alter { @@ -22,10 +35,39 @@ public class DingTalkAlter implements Alter { private String profile; @Resource private SupportRefreshProperties refreshProperties; + @Resource + private DingDingMsgApi dingDingMsgApi; @Override public void invoke(AlterDTO alterDTO) { - DingTalkUtils.sendDingTalkForBizNodeAlter(profile, alterDTO, refreshProperties.getAlterMobiles()); + log.info("send biz node alter : {}", JSON.toJSONString(alterDTO)); + if (Objects.equals(profile, "master")) { + DingTalkUtils.sendDingTalkForBizNodeAlter(profile, alterDTO, refreshProperties.getAlterMobiles()); + } else { + rivenDingtalk(alterDTO); + } + } + + private void rivenDingtalk(AlterDTO alterDTO) { + DingDingSendRebootGroupMsgReq req = new DingDingSendRebootGroupMsgReq(); + req.setDingDingScene("WORKFLOW_ENGINE_BIZNODE_ALTER"); + String processInstanceId = alterDTO.getProcessInstanceId(); + String title = "Notice 业务节点长时间停止告警, Env: " + profile; + String text = "#### [" + profile + "]业务节点长时间停止\n" + + "> 节点相关信息: " + JSONUtil.toJsonStr(alterDTO) + "\n\n" + + "> ##### [点击查看审批日志](" + getWebUrl(profile) + "/#/workflow/examples?processInstanceId=" + processInstanceId + ") \n\n" + + "> ##### 提示:如果以上地址提示未登录,请在对应环境 OMS 系统登录后并点击审批管理的功能后,再使用。或者复制实例 ID 到 OMS 中手动查询 \n\n " + + mobiles(refreshProperties.getAlterMobiles()); + SampleMarkdown markdown = new SampleMarkdown(title, text); + + JSONObject markdownJson = JSONObject.parseObject(markdown.toJson()); + JSONObject atMobiles = new JSONObject(); + atMobiles.put("atMobiles", refreshProperties.getAlterMobiles()); + markdownJson.put("at", atMobiles); + markdownJson.put("isAtAll", false); + req.setDingDingJson(markdownJson.toJSONString()); + req.setMsgType(DingTalkMsgTypeEnum.sampleMarkdown); + dingDingMsgApi.sendRebootGroupMsg(req); } } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/ShellUtil.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/ShellUtil.java index 08cb90329..07834b6c3 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/ShellUtil.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/ShellUtil.java @@ -1,7 +1,6 @@ package cn.axzo.workflow.server.common.util; import cn.hutool.core.date.DateUtil; -import liquibase.pro.packaged.D; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/WpsUtil.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/WpsUtil.java new file mode 100644 index 000000000..6c892eb24 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/util/WpsUtil.java @@ -0,0 +1,211 @@ +package cn.axzo.workflow.server.common.util; + +import cn.axzo.basics.common.BeanMapper; +import cn.axzo.maokai.api.client.OrganizationalNodeUserQueryApi; +import cn.axzo.maokai.api.vo.request.OrgNodeUserBriefInfoListReq; +import cn.axzo.maokai.api.vo.response.OrgNodeUserBriefInfoResp; +import cn.axzo.nanopart.doc.api.domain.FileReplaceContent; +import cn.axzo.nanopart.doc.api.filetemplate.FileTemplateApi; +import cn.axzo.nanopart.doc.api.filetemplate.request.FileTemplateReplaceRequest; +import cn.axzo.nanopart.doc.api.filetemplate.request.SimpleFileTemplateReplaceRequest; +import cn.axzo.nanopart.doc.api.index.request.DeleteNodeRequest; +import cn.axzo.oss.http.api.ServerFileServiceApi; +import cn.axzo.oss.http.model.ApiSignUrlDownloadRequest; +import cn.axzo.oss.http.model.ApiSignUrlDownloadResponse; +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.common.model.dto.SignatureDTO; +import cn.axzo.workflow.common.model.dto.UploadFieldDTO; +import cn.axzo.workflow.common.model.dto.VariableObjectDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.core.engine.cmd.CustomGetProcessInstanceVariablesToObjectCmd; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.google.common.collect.Lists; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_NAME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_NAME_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_PHONE; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC; +import static cn.axzo.workflow.common.model.dto.VariableObjectDTO.Type.signatureAndAdvice; + +/** + * WPS 工具类 + * + * @author wangli + * @since 2025-04-09 20:41 + */ +@Component +public class WpsUtil { + public static final List WPS_SUPPORTED_FILE_TYPES = Lists.newArrayList(FileTypeEnum.WORD, FileTypeEnum.EXCEL); + @Resource + private FileTemplateApi fileTemplateApi; + @Resource + private ServerFileServiceApi serverFileServiceApi; + @Resource + private OrganizationalNodeUserQueryApi organizationalNodeUserQueryApi; + + /** + * 调用 wps 文件变量替换接口 + * + * @param fileCode 基于该模板进行变量替换 + * @param fileKey 如果有值,则变量替换完成后,直接将文档覆盖该 fileKey 对应的文件; + * @return 返回替换变量后的文件oss 的 fileKey + */ + public String wpsFileVariableReplace(List wpsVariables, + String fileCode, String fileKey, String fileName) { + + List fileReplaceContents = BeanMapper.copyList(wpsVariables.stream() + .filter(i -> Objects.nonNull(i.getValue())) + .filter(i -> !(Objects.equals(i.getType().name(), "img") && !StringUtils.hasText(i.getValue().toString()))) + .filter(i -> Objects.equals(i.getType().name(), "img") || Objects.equals(i.getType().name(), "text")) + .collect(Collectors.toList()), FileReplaceContent.class, (s, t) -> { + t.setKey(s.getDesc()); + if (Objects.equals(s.getType().name(), "img")) { + if (isJson(s.getValue().toString())) { + t.setContent(JSON.parseArray(s.getValue().toString(), UploadFieldDTO.class).get(0).getFileUrl()); + } else { + t.setContent(JSON.parseArray(JSON.toJSONString(s.getValue()), UploadFieldDTO.class).get(0).getFileUrl()); + } + } else { + t.setContent(Objects.nonNull(s.getValue()) ? s.getValue().toString() : ""); + } + t.setType(s.getType().name()); + t.setPrefix("["); + t.setSuffix("]"); + }); + if (StringUtils.hasText(fileCode)) { + FileTemplateReplaceRequest request = new FileTemplateReplaceRequest(); + request.setFileCode(fileCode); + request.setFileKey(fileKey); + request.setReplaceContentList(fileReplaceContents); + return RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.replaceWordText(request), "替换 WPS 文档变量", request); + } else { + SimpleFileTemplateReplaceRequest request = new SimpleFileTemplateReplaceRequest(); + request.setFileName(fileName); + request.setFileKey(fileKey); + request.setReplaceContentList(fileReplaceContents); + return RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.replaceWordTextForFileKey(request), "替换 WPS 文档变量(simple)", request); + } + } + + public List getWpsReplaceVariables(ProcessEngineConfigurationImpl processEngineConfiguration, String processInstanceId) { + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + List wpsVariables = + commandExecutor.execute(new CustomGetProcessInstanceVariablesToObjectCmd(processInstanceId)); + + List signatureAndAdvices = Lists.newArrayList(); + wpsVariables.stream().filter(i -> Objects.equals(signatureAndAdvice, i.getType())).forEach(variableObjectDTO -> { + List signDetails = (List) variableObjectDTO.getValue(); + if (!CollectionUtils.isEmpty(signDetails)) { + SignatureDTO.SignDetail signDetail = signDetails.get(0); + ApiSignUrlDownloadRequest request = ApiSignUrlDownloadRequest.builder() + .fileKeys(Lists.newArrayList(signDetail.getSignature())).build(); + List signUrl = RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(request), "获取手写签图片地址", request); + signatureAndAdvices.add(VariableObjectDTO.builder() + .key(variableObjectDTO.getKey() + "_signature") + .desc(variableObjectDTO.getDesc() + "电子签名") + .value(Lists.newArrayList(UploadFieldDTO.builder().fileKey(signDetail.getSignature()).fileUrl(signUrl.get(0).getSignUrl()).build())) + .type(VariableObjectDTO.Type.img) + .build()); + signatureAndAdvices.add(VariableObjectDTO.builder() + .key(variableObjectDTO.getKey() + "_advice") + .desc(variableObjectDTO.getDesc() + "审批意见") + .value(signDetail.getAdvice()) + .type(VariableObjectDTO.Type.text) + .build()); + } + }); + wpsVariables.addAll(signatureAndAdvices); + + wpsVariables.stream().filter(i -> Objects.equals(i.getKey(), PRINT_VAR_PROCESS_INITIATOR)).findAny().ifPresent(i -> { + BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(i.getValue()); + Optional user = getUserInfo(initiator); + wpsVariables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_INITIATOR_NAME) + .desc(PRINT_VAR_PROCESS_INITIATOR_NAME_DESC) + .value(user.isPresent() ? user.get().getRealName() : "") + .build()); + wpsVariables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_INITIATOR_POSITION) + .desc(PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC) + .value(user.isPresent() && Objects.nonNull(user.get().getJob()) ? user.get().getJob().getName() : "") + .build()); + wpsVariables.add(VariableObjectDTO.builder() + .key(PRINT_VAR_PROCESS_INITIATOR_PHONE) + .desc(PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC) + .value(user.isPresent() ? user.get().getProfile().getPhone() : "") + .build()); + + }); + return wpsVariables; + } + + public void deleteWpsFile(String wpsCode) { + if (!StringUtils.hasText(wpsCode)) { + return; + } + DeleteNodeRequest request = new DeleteNodeRequest(); + request.setCode(wpsCode); + RpcExternalUtil.rpcProcessor(() -> fileTemplateApi.delete(request), "删除 WPS 文档", request); + } + + public static boolean isJson(String str) { + try { + JSON.parse(str); + return true; + } catch (JSONException e) { + return false; + } + } + + public static boolean isWpsFile(FileTypeEnum fileTypeEnum) { + return WPS_SUPPORTED_FILE_TYPES.contains(fileTypeEnum); + } + + private Optional getUserInfo(BpmnTaskDelegateAssigner assigner) { + if (Objects.isNull(assigner)) { + return Optional.empty(); + } + OrgNodeUserBriefInfoListReq req = new OrgNodeUserBriefInfoListReq(); + if (StringUtils.hasText(assigner.getTenantId())) { + req.setWorkspaceId(Long.valueOf(assigner.getTenantId())); + } + if (StringUtils.hasText(assigner.getOuId())) { + req.setOuId(Long.valueOf(assigner.getOuId())); + } + if (StringUtils.hasText(assigner.getNodeId())) { + req.setOrgNodeIds(Lists.newArrayList(Long.valueOf(assigner.getNodeId()))); + } + if (StringUtils.hasText(assigner.getPersonId())) { + req.setPersonIds(Lists.newArrayList(Long.valueOf(assigner.getPersonId()))); + } + req.setNeedJob(true); + req.setNeedUnit(true); + req.setNeedProfile(true); + req.setContainsExited(true); + List users = RpcExternalUtil.rpcApiResultProcessor(() -> organizationalNodeUserQueryApi.listOrgNodeUsers(req), + "查询审批人员组织信息", req); + + if (CollectionUtils.isEmpty(users)) { + return Optional.empty(); + } + + return users.stream().sorted(Comparator.comparing(OrgNodeUserBriefInfoResp::getExited).reversed()) + .collect(Collectors.toList()).stream().findFirst(); + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/conf/ShutdownConfig.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/conf/ShutdownConfig.java new file mode 100644 index 000000000..ef8f41c03 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/conf/ShutdownConfig.java @@ -0,0 +1,29 @@ +package cn.axzo.workflow.server.conf; + +import cn.axzo.workflow.core.conf.CustomEventManager; +import org.springframework.context.annotation.Configuration; + +import javax.annotation.PreDestroy; + +/** + * 应用内事件处理 + * + * @author wangli + * @since 2025-04-07 17:09 + */ +@Configuration +public class ShutdownConfig { + + private final CustomEventManager eventManager; + + public ShutdownConfig(CustomEventManager eventManager) { + this.eventManager = eventManager; + } + + @PreDestroy + public void close() throws InterruptedException { + System.out.println("Closing application, waiting for events to be processed..."); + eventManager.waitForEventsToBeProcessed(); + System.out.println("All events have been processed."); + } +} \ No newline at end of file diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/AbstractBpmnTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/AbstractBpmnTaskAssigneeSelector.java index 22ee2f85c..8833a7101 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/AbstractBpmnTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/AbstractBpmnTaskAssigneeSelector.java @@ -63,6 +63,11 @@ public abstract class AbstractBpmnTaskAssigneeSelector implements BpmnTaskAssign protected SupportRefreshProperties refreshProperties; private ApplicationContext applicationContext; + @Override + public boolean support(String param) { + return getType().equals(param); + } + @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { @@ -229,7 +234,7 @@ public abstract class AbstractBpmnTaskAssigneeSelector implements BpmnTaskAssign } List personIds = assigners.stream() .map(BpmnTaskDelegateAssigner::getPersonId) - .filter(e -> Objects.nonNull(e) && StringUtils.hasText(e) && !Objects.equals("null", e)) + .filter(e -> Objects.nonNull(e) && StringUtils.hasText(e) && !Objects.equals("null", e) && !Objects.equals("system", e)) .map(Long::parseLong) .distinct().collect(Collectors.toList()); if (CollectionUtils.isEmpty(personIds)) { @@ -239,12 +244,12 @@ public abstract class AbstractBpmnTaskAssigneeSelector implements BpmnTaskAssign "根据 PersonId 查询自然人档案", "cn.axzo.karma.client.feign.FlowSupportApi.listPersons", refreshProperties, context, personIds) .stream().collect(Collectors.toMap(PersonProfileResp::getId, Function.identity(), (s, t) -> s)); assigners.forEach(assigner -> { - if (!StringUtils.hasText(assigner.getPersonId()) || Objects.equals("null", assigner.getPersonId())) { + if (!StringUtils.hasText(assigner.getPersonId()) || Objects.equals("null", assigner.getPersonId()) || Objects.equals("system", assigner.getPersonId())) { return; } long personId = Long.parseLong(Optional.ofNullable(assigner.getPersonId()).orElse("-1")); if (personProfileMap.containsKey(personId)) { - log.warn("查询到的人员信息:{}", JSON.toJSONString(personProfileMap.getOrDefault(personId, null))); + log.info("查询到的人员信息:{}", JSON.toJSONString(personProfileMap.getOrDefault(personId, null))); assigner.setAvatar(personProfileMap.getOrDefault(personId, new PersonProfileResp()).getAvatarUrl()); assigner.setAssignerName(personProfileMap.getOrDefault(personId, new PersonProfileResp()).getRealName()); diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedFixedPersonTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedFixedPersonTaskAssigneeSelector.java index 53be28175..a1b2fcd66 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedFixedPersonTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedFixedPersonTaskAssigneeSelector.java @@ -18,7 +18,11 @@ import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.annotation.Resource; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import static cn.axzo.workflow.common.code.FlowableEngineRespCode.ENGINE_USER_TASK_CALC_ERROR; @@ -39,11 +43,6 @@ public class BasedFixedPersonTaskAssigneeSelector extends AbstractBpmnTaskAssign return ApproverSpecifyEnum.fixedPerson.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedIdentityTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedIdentityTaskAssigneeSelector.java index 918c871ba..a699ca334 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedIdentityTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedIdentityTaskAssigneeSelector.java @@ -31,11 +31,6 @@ public class BasedIdentityTaskAssigneeSelector extends AbstractBpmnTaskAssigneeS return ApproverSpecifyEnum.identity.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderRecursionTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderRecursionTaskAssigneeSelector.java index 83919b024..54ad51f0c 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderRecursionTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderRecursionTaskAssigneeSelector.java @@ -1,8 +1,8 @@ package cn.axzo.workflow.server.controller.delegate; import cn.axzo.workflow.common.enums.ApproverSpecifyEnum; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import org.flowable.bpmn.model.FlowElement; import org.flowable.engine.delegate.DelegateExecution; import org.springframework.stereotype.Component; @@ -27,11 +27,6 @@ public class BasedInitiatorLeaderRecursionTaskAssigneeSelector extends AbstractB return ApproverSpecifyEnum.initiatorLeaderRecursion.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderTaskAssigneeSelector.java index 040b802c7..3c7267fb9 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorLeaderTaskAssigneeSelector.java @@ -40,11 +40,6 @@ public class BasedInitiatorLeaderTaskAssigneeSelector extends AbstractBpmnTaskAs return ApproverSpecifyEnum.initiatorLeader.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorSpecifiedTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorSpecifiedTaskAssigneeSelector.java new file mode 100644 index 000000000..a6dfb8885 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedInitiatorSpecifiedTaskAssigneeSelector.java @@ -0,0 +1,84 @@ +package cn.axzo.workflow.server.controller.delegate; + +import cn.axzo.workflow.common.enums.ApproverSpecifyEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.FlowableEngineRespCode.ENGINE_USER_TASK_CALC_ERROR; +import static cn.axzo.workflow.common.constant.BpmnConstants.INITIATOR_SPECIFY; + +/** + * 当节点配置为发起时指定,就使用当前类进行解析处理审批人逻辑 + * + * @author wangli + * @since 2025-01-10 14:32 + */ +@Slf4j +@Component +@AllArgsConstructor +public class BasedInitiatorSpecifiedTaskAssigneeSelector extends AbstractBpmnTaskAssigneeSelector { + + private final RuntimeService runtimeService; + + @Override + public String getType() { + return ApproverSpecifyEnum.initiatorSpecified.getType(); + } + + @Override + public boolean support(String param) { + return getType().equals(param) || ApproverSpecifyEnum.signerRelated.getType().equals(param); + } + + @Override + public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { + List assigners = new ArrayList<>(); + try { + populateAssigneeByInitiatorSpecify(assigners, flowElement, execution); + } catch (Throwable t) { + if (Boolean.TRUE.equals(throwException)) { + log.warn("执行查询发起时指定审批人时发现异常, 审批节点:{}, 异常信息:{}", flowElement.getId(), t.getMessage()); + if (!(t instanceof WorkflowEngineException)) { + throw new WorkflowEngineException(ENGINE_USER_TASK_CALC_ERROR, flowElement.getId(), + this.getType(), t.getMessage()); + } + throw t; + } else { + return Collections.emptyList(); + } + } + return assigners.stream().filter(i -> StringUtils.hasText(i.getPersonId())).collect(Collectors.toList()); + } + + /** + * 创建审批时,传入各个节点应该设置的审批人的数据信息,此处直接设置人 + * + * @param assigners + * @param flowElement + * @param execution + */ + private void populateAssigneeByInitiatorSpecify(List assigners, FlowElement flowElement, DelegateExecution execution) { + String processInstanceId = execution.getProcessInstanceId(); + @SuppressWarnings("unchecked") + Map> initiatorSpecifyMap = runtimeService.getVariable(processInstanceId, INITIATOR_SPECIFY, Map.class); + if (CollectionUtils.isEmpty(initiatorSpecifyMap)) { + return; + } + + assigners.addAll(initiatorSpecifyMap.getOrDefault(flowElement.getId(), Collections.emptyList())); + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedPositionTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedPositionTaskAssigneeSelector.java index b708547b7..29e894e2c 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedPositionTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedPositionTaskAssigneeSelector.java @@ -36,11 +36,6 @@ public class BasedPositionTaskAssigneeSelector extends AbstractBpmnTaskAssigneeS return ApproverSpecifyEnum.position.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedRoleTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedRoleTaskAssigneeSelector.java index a4eab6945..3149aad66 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedRoleTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BasedRoleTaskAssigneeSelector.java @@ -13,10 +13,10 @@ import cn.hutool.core.collection.CollUtil; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.FlowElement; import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import javax.annotation.Resource; import java.util.Collections; import java.util.List; import java.util.Map; @@ -36,10 +36,10 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_MODEL_CATEGORY; @Component public class BasedRoleTaskAssigneeSelector extends AbstractBpmnTaskAssigneeSelector { - @Autowired + @Resource private CategoryService categoryService; - @Autowired + @Resource private BpmnProcessDefinitionService bpmnProcessDefinitionService; @Override @@ -47,11 +47,6 @@ public class BasedRoleTaskAssigneeSelector extends AbstractBpmnTaskAssigneeSelec return ApproverSpecifyEnum.role.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/PreNodeSpecifiedTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/PreNodeSpecifiedTaskAssigneeSelector.java index b6ee4403b..d7d76d3b9 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/PreNodeSpecifiedTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/PreNodeSpecifiedTaskAssigneeSelector.java @@ -32,11 +32,6 @@ public class PreNodeSpecifiedTaskAssigneeSelector extends AbstractBpmnTaskAssign return ApproverSpecifyEnum.preNodeSpecified.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/TransferToAdminTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/TransferToAdminTaskAssigneeSelector.java index 20089b4e6..274d7ec2e 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/TransferToAdminTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/TransferToAdminTaskAssigneeSelector.java @@ -19,13 +19,11 @@ import org.apache.commons.collections4.ListUtils; import org.flowable.bpmn.model.FlowElement; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; -import javax.validation.Valid; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -55,11 +53,6 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne return ApproverEmptyHandleTypeEnum.transferToAdmin.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { @@ -70,7 +63,7 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne protected List invokeService(FlowElement flowElement, DelegateExecution execution, ApproverScopeDTO scopeDto) { - if(Boolean.TRUE.equals(supportRefreshProperties.getUseNewToAdminApi())) { + if (Boolean.TRUE.equals(supportRefreshProperties.getUseNewToAdminApi())) { return invokeNewQuery(flowElement, execution, scopeDto); } else { return invokeOldQuery(flowElement, execution, scopeDto); @@ -90,7 +83,10 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne .map(w -> BeanUtil.copyProperties(w, ListFlowTaskAssignerReq.OrgScope.class)) .collect(Collectors.toList())); } - ListFlowTaskAssignerReq req = builder.workspaceAdmin(false).build(); + ListFlowTaskAssignerReq req = builder + .procInstId(execution.getProcessInstanceId()) + .workspaceAdmin(false) + .build(); BpmnMetaParserHelper.getApproverScope((UserTask) flowElement) .ifPresent(i -> { if (Objects.equals(i, ApproverScopeEnum.projectWorkspace)) { @@ -133,13 +129,14 @@ public class TransferToAdminTaskAssigneeSelector extends AbstractBpmnTaskAssigne } ListFlowTaskAssignerReq req = builder.workspaceAdmin(false).build(); req.setProcInstId(execution.getProcessInstanceId()); - if(Objects.equals(ApproverScopeEnum.entWorkspace, approverScope.get()) && Objects.equals(ApproverSpecifyEnum.fixedPerson,optSpecify.get())) { - req.setCooperateTypes(Sets.newHashSet(1,2,3,4,5,6,7,8,9,11,30)); + if (approverScope.isPresent() && Objects.equals(ApproverScopeEnum.entWorkspace, approverScope.get()) && + optSpecify.isPresent() && Objects.equals(ApproverSpecifyEnum.fixedPerson, optSpecify.get())) { + req.setCooperateTypes(Sets.newHashSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 30)); } else { req.setCooperateTypes(getCooperationTypes(flowElement)); } // 发起人主管找其超管时,需要将发起人的数据包装进 orgScope - if (Objects.equals(ApproverSpecifyEnum.initiatorLeader, optSpecify.get())) { + if (optSpecify.isPresent() && Objects.equals(ApproverSpecifyEnum.initiatorLeader, optSpecify.get())) { BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(execution.getVariable(INTERNAL_INITIATOR, String.class)); Integer workspaceType = execution.getVariable(INTERNAL_PROCESS_WORKSPACE_TYPE, Integer.class); req.setOrgScopes(Lists.newArrayList(new ListFlowTaskAssignerReq.OrgScope(workspaceType, Long.parseLong(initiator.getTenantId()), Long.parseLong(initiator.getOuId()), null))); diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/SpecifyAssigneeTaskAssigneeSelector.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/TransferToSpecifyTaskAssigneeSelector.java similarity index 94% rename from workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/SpecifyAssigneeTaskAssigneeSelector.java rename to workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/TransferToSpecifyTaskAssigneeSelector.java index 29179d144..db97aa496 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/SpecifyAssigneeTaskAssigneeSelector.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/TransferToSpecifyTaskAssigneeSelector.java @@ -19,17 +19,21 @@ import org.flowable.engine.delegate.DelegateExecution; import org.springframework.stereotype.Component; import javax.annotation.Resource; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.stream.Collectors; import static cn.axzo.workflow.common.code.FlowableEngineRespCode.ENGINE_USER_TASK_CALC_ERROR; /** - * 转交指定人查询人 + * 转交给指定人 */ @Slf4j @Component -public class SpecifyAssigneeTaskAssigneeSelector extends AbstractBpmnTaskAssigneeSelector { +public class TransferToSpecifyTaskAssigneeSelector extends AbstractBpmnTaskAssigneeSelector { @Resource private OrgNodeUserApi orgNodeUserApi; @@ -39,11 +43,6 @@ public class SpecifyAssigneeTaskAssigneeSelector extends AbstractBpmnTaskAssigne return ApproverEmptyHandleTypeEnum.specifyAssignee.getType(); } - @Override - public boolean support(String param) { - return getType().equals(param); - } - @Override public List select(FlowElement flowElement, DelegateExecution execution, Boolean throwException) { if (!(flowElement instanceof UserTask)) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/FirstCopyTemplateFileActivityEvent_101_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/FirstCopyTemplateFileActivityEvent_101_Listener.java new file mode 100644 index 000000000..fef8d0e23 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/FirstCopyTemplateFileActivityEvent_101_Listener.java @@ -0,0 +1,165 @@ +//package cn.axzo.workflow.server.controller.listener.activity; +// +//import cn.axzo.nanopart.doc.api.anonymous.DocAnonymousDatabaseApi; +//import cn.axzo.nanopart.doc.api.index.request.CopyNodeRequest; +//import cn.axzo.workflow.common.enums.FileTypeEnum; +//import cn.axzo.workflow.common.model.dto.SignFileDTO; +//import cn.axzo.workflow.common.model.dto.VariableObjectDTO; +//import cn.axzo.workflow.common.model.request.bpmn.BpmnSignConf; +//import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCloneDTO; +//import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +//import cn.axzo.workflow.core.common.context.ActivityOperationContext; +//import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; +//import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd; +//import cn.axzo.workflow.core.listener.AbstractBpmnEventListener; +//import cn.axzo.workflow.core.listener.BpmnActivityEventListener; +//import cn.axzo.workflow.core.repository.entity.ExtAxProcessSign; +//import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper; +//import cn.axzo.workflow.core.service.ExtAxModelDocService; +//import cn.axzo.workflow.core.service.ExtAxProcessSignService; +//import cn.axzo.workflow.core.service.ExtAxReModelService; +//import cn.axzo.workflow.server.common.util.RpcExternalUtil; +//import cn.axzo.workflow.server.common.util.WpsUtil; +//import lombok.extern.slf4j.Slf4j; +//import org.flowable.bpmn.model.Process; +//import org.flowable.common.engine.impl.interceptor.CommandExecutor; +//import org.flowable.engine.delegate.DelegateExecution; +//import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +//import org.flowable.engine.impl.util.CommandContextUtil; +//import org.flowable.engine.impl.util.ProcessDefinitionUtil; +//import org.springframework.context.annotation.Scope; +//import org.springframework.core.Ordered; +//import org.springframework.stereotype.Component; +//import org.springframework.util.CollectionUtils; +// +//import javax.annotation.Resource; +//import java.util.ArrayList; +//import java.util.Collections; +//import java.util.List; +//import java.util.Objects; +//import java.util.Optional; +// +//import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; +// +/// ** +// * 签署业务才会执行 +// *

+// * 流程实例启用成功后: +// * 1、将模型关联的且实例勾选使用过的文档进行原始模板复制 +// * 2、进行初始数据的模板变量替换 +// *

+// * 并存至 ext_ax_process_sign 中的 doc_template 字段 +// * +// * @author wangli +// * @since 2025-04-23 14:32 +// */ +//@Slf4j +////@Component +//@Scope("prototype") +//public class FirstCopyTemplateFileActivityEvent_101_Listener extends AbstractBpmnEventListener implements BpmnActivityEventListener, Ordered { +// +// @Resource +// private ExtAxProcessSignService extAxProcessSignService; +// @Resource +// private ExtAxModelDocMapper extAxModelDocMapper; +// @Resource +// private ExtAxReModelService extAxReModelService; +// @Resource +// private ExtAxModelDocService extAxModelDocService; +// @Resource +// private DocAnonymousDatabaseApi docAnonymousApi; +// @Resource +// private WpsUtil wpsUtil; +// +// @Override +// public int getOrder() { +// return Integer.MIN_VALUE + 101; +// } +// +// @Override +// public void onStart(DelegateExecution execution) { +// if (!Objects.equals(NODE_STARTER.getType(), execution.getCurrentActivityId())) { +// return; +// } +// String processInstanceId = execution.getProcessInstanceId(); +// +// Process mainProcess = ProcessDefinitionUtil.getBpmnModel(execution.getProcessDefinitionId()).getMainProcess(); +// Optional signConfig = BpmnMetaParserHelper.getSignConfig(mainProcess); +// if (!signConfig.isPresent() || Objects.isNull(signConfig.get().getSignType())) { +// return; +// } +// +// ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); +// CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); +// List docs = commandExecutor.execute(new CustomGetModelDocsCmd(processInstanceId, true, extAxModelDocMapper, extAxReModelService)); +// ExtAxProcessSign processSign = new ExtAxProcessSign(); +// processSign.setProcessInstanceId(processInstanceId); +// processSign.setSignType(signConfig.get().getSignType().getType()); +// processSign.setPendingMessageId(signConfig.get().getSignPendingProperty().getPendingMessageId()); +// if (CollectionUtils.isEmpty(docs)) { +// processSign.setDocTemplate(Collections.emptyList()); +// processSign.setFileArchive(Collections.emptyList()); +// } else { +// // 复制基础模板 +// List docTemplates = copyTempTemplate(docs); +// processSign.setDocTemplate(docTemplates); +// +// List archives = replaceTemplateVariable(docTemplates, processEngineConfiguration, processInstanceId); +// processSign.setFileArchive(archives); +// } +// // 没有可用的文档,但仍然记录库表 +// extAxProcessSignService.save(processSign); +// } +// +// private List replaceTemplateVariable(List docTemplates, ProcessEngineConfigurationImpl processEngineConfiguration, String processInstanceId) { +// List archives = new ArrayList<>(); +// List wpsReplaceVariables = wpsUtil.getWpsReplaceVariables(processEngineConfiguration, processInstanceId); +// docTemplates.forEach(template -> { +// SignFileDTO signFileDTO = new SignFileDTO(); +// signFileDTO.setId(template.getId()); +// signFileDTO.setFileName(template.getFileName()); +// signFileDTO.setTemplateName(template.getTemplateName()); +// signFileDTO.setFileTag(template.getFileTag()); +// signFileDTO.setFileType(template.getFileType()); +// signFileDTO.setFileCode(template.getFileCode()); +// if (Objects.equals(template.getFileType(), FileTypeEnum.WORD) || Objects.equals(template.getFileType(), FileTypeEnum.EXCEL)) { +// String fileKey = wpsUtil.wpsFileVariableReplace(wpsReplaceVariables, template.getFileCode(), null, template.getTemplateName() + template.getFileType().getSuffix()); +// signFileDTO.setFileKey(fileKey); +// } +// archives.add(signFileDTO); +// }); +// return archives; +// } +// +// private List copyTempTemplate(List docs) { +// List files = new ArrayList<>(); +// docs.forEach(doc -> { +// SignFileDTO signFileDTO = new SignFileDTO(); +// // 原始文档 ID +// signFileDTO.setId(doc.getId()); +// signFileDTO.setFileName(doc.getFileName()); +// signFileDTO.setTemplateName(doc.getTemplateName()); +// signFileDTO.setFileTag(doc.getTag()); +// signFileDTO.setFileType(doc.getFileType()); +// switch (doc.getFileType()) { +// case WORD: +// case EXCEL: +// CopyNodeRequest copy = new CopyNodeRequest(); +// copy.setCode(doc.getFileRelationId()); +// String fileCode = RpcExternalUtil.rpcProcessor(() -> docAnonymousApi.copy(copy), "复制文档", copy); +// signFileDTO.setFileCode(fileCode); +// break; +// case HIPRINT: +// Long newDocId = extAxModelDocService.cloneDoc(DocCloneDTO.builder().docId(doc.getId()).build(), true); +// signFileDTO.setFileCode(String.valueOf(newDocId)); +// break; +// case PDF: +// break; +// default: +// break; +// } +// files.add(signFileDTO); +// }); +// return files; +// } +//} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/OperationFileArchiveActivityEvent_101_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/OperationFileArchiveActivityEvent_101_Listener.java new file mode 100644 index 000000000..2afc96ca8 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/OperationFileArchiveActivityEvent_101_Listener.java @@ -0,0 +1,72 @@ +package cn.axzo.workflow.server.controller.listener.activity; + +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.dto.VariableObjectDTO; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignConf; +import cn.axzo.workflow.core.common.context.ActivityOperationContext; +import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; +import cn.axzo.workflow.core.listener.AbstractBpmnEventListener; +import cn.axzo.workflow.core.listener.BpmnActivityEventListener; +import cn.axzo.workflow.core.repository.entity.ExtAxProcessSign; +import cn.axzo.workflow.core.service.ExtAxProcessSignService; +import cn.axzo.workflow.server.common.util.WpsUtil; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.impl.util.ProcessDefinitionUtil; +import org.springframework.context.annotation.Scope; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_SIGN_DATA_NOT_EXISTS; + +/** + * 每个审批节点运行结束后,临时替换一次变量,并更新至 ext_ax_process_sign 中 file_archive 字段 + * + * @author wangli + * @since 2025-04-03 11:05 + */ +@Slf4j +@Component +@Scope("prototype") +public class OperationFileArchiveActivityEvent_101_Listener extends AbstractBpmnEventListener implements BpmnActivityEventListener, Ordered { + @Resource + private ExtAxProcessSignService extAxProcessSignService; + @Resource + private WpsUtil wpsUtil; + + @Override + public int getOrder() { + return Integer.MIN_VALUE + 101; + } + + @Override + public void onEnd(DelegateExecution execution) { + Process process = ProcessDefinitionUtil.getProcess(execution.getProcessDefinitionId()); + Optional signConfig = BpmnMetaParserHelper.getSignConfig(process); + if (!signConfig.isPresent() || Objects.isNull(signConfig.get().getSignType())) { + return; + } + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); + ExtAxProcessSign processSign = extAxProcessSignService.findByProcessInstanceId(execution.getProcessInstanceId()); + if (Objects.isNull(processSign)) { + throw new WorkflowEngineException(PROCESS_SIGN_DATA_NOT_EXISTS); + } + List wpsReplaceVariables = wpsUtil.getWpsReplaceVariables(processEngineConfiguration, execution.getProcessInstanceId()); + processSign.getFileArchive().stream().filter(i -> Objects.equals(i.getFileType(), FileTypeEnum.WORD) + || Objects.equals(i.getFileType(), FileTypeEnum.EXCEL)) + .forEach(docBaseVO -> { + String newFileKey = wpsUtil.wpsFileVariableReplace(wpsReplaceVariables, null, docBaseVO.getFileKey(), docBaseVO.getTemplateName() + docBaseVO.getFileType().getSuffix()); + docBaseVO.setFileKey(newFileKey); + }); + extAxProcessSignService.updateById(processSign); + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEvent_100_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEvent_100_Listener.java index 5812c80db..f4d2a6e37 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEvent_100_Listener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEvent_100_Listener.java @@ -37,7 +37,9 @@ import java.util.stream.Collectors; import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT; +import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_APPLICATION; import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_PROCESS_DEFINITION_KEY; +import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_OWNERSHIP_APPLICATION; import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED; import static cn.axzo.workflow.common.enums.ProcessActivityEventEnum.PROCESS_ACTIVITY_CALLBACK; @@ -215,9 +217,16 @@ public class RocketMqBpmActivityEvent_100_Listener extends AbstractBpmnEventList } Map header = new HashMap<>(); if (StringUtils.hasText(dto.getProcessDefinitionKey())) { - log.warn("record process definition key: {}", dto.getProcessDefinitionKey()); + log.info("record process definition key: {}", dto.getProcessDefinitionKey()); header.put(MQ_OWNERSHIP_PROCESS_DEFINITION_KEY, dto.getProcessDefinitionKey()); } + if (dto.getVariables().containsKey(PROCESS_OWNERSHIP_APPLICATION)) { + Object orDefault = dto.getVariables().getOrDefault(PROCESS_OWNERSHIP_APPLICATION, ""); + if (Objects.nonNull(orDefault)) { + log.info("record process ownership app name: {}", orDefault); + header.put(MQ_OWNERSHIP_APPLICATION, orDefault.toString()); + } + } eventProducer.send(Event.builder() .shardingKey(dto.getProcessInstanceId()) .eventCode(eventEnum.getEventCode()) diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/doc/DocChangeListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/doc/DocChangeListener.java new file mode 100644 index 000000000..375fe0009 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/doc/DocChangeListener.java @@ -0,0 +1,54 @@ +package cn.axzo.workflow.server.controller.listener.doc; + +import cn.axzo.framework.rocketmq.Event; +import cn.axzo.framework.rocketmq.EventProducer; +import cn.axzo.workflow.common.enums.DocChangeEventEnum; +import cn.axzo.workflow.core.conf.CustomEventManager; +import cn.axzo.workflow.core.engine.event.DocChangeEvent; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import javax.annotation.Resource; + +/** + * Application 内的文档变更事件处理器,通过该接口广播 MQ + * + * @author wangli + * @since 2025-04-07 17:10 + */ +@Component +public class DocChangeListener { + @Resource + private CustomEventManager eventManager; + @Resource + private EventProducer eventProducer; + @Value("${sendMq:true}") + private Boolean sendMQ; + + @TransactionalEventListener(value = DocChangeEvent.class, phase = TransactionPhase.AFTER_COMMIT) + public void handleEvent(DocChangeEvent event) { + try { + sendMessageQueue(event); + } finally { + eventManager.eventProcessed(); + } + } + + + public void sendMessageQueue(DocChangeEvent event) { + if (!sendMQ) { + return; + } + eventProducer.send(Event.builder() + .shardingKey(event.getKey()) + .eventCode(DocChangeEventEnum.DOC_CHANGE.getEventCode()) + .targetId(event.getKey()) + .targetType(event.getKey()) + .data(JSON.toJSONString(event, SerializerFeature.WriteMapNullValue)) + .build()); + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java index 49a6cafa9..4906d2cee 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java @@ -56,10 +56,12 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TY import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_SPECIFY_NEXT_APPROVER; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO; -import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_PROCESS_DEFINITION_KEY; import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_ASSIGNER_BATCH_SIZE; +import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_APPLICATION; +import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_PROCESS_DEFINITION_KEY; import static cn.axzo.workflow.common.constant.BpmnConstants.MULTI_INSTANCE_LOOP_COUNTER; import static cn.axzo.workflow.common.constant.BpmnConstants.NUMBER_OF_INSTANCES; +import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_OWNERSHIP_APPLICATION; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; import static cn.axzo.workflow.common.constant.VariableConstants.VAR_ACTIVITY_ID; import static cn.axzo.workflow.common.constant.VariableConstants.VAR_ACTIVITY_NAME; @@ -78,6 +80,7 @@ import static cn.axzo.workflow.common.constant.VariableConstants.VAR_TASK_START_ import static cn.axzo.workflow.common.constant.VariableConstants.VAR_TASK_USER_NAME; import static cn.axzo.workflow.common.enums.ProcessMessagePushEventEnum.PROCESS_CARBON_COPY; import static cn.axzo.workflow.common.enums.ProcessMessagePushEventEnum.PROCESS_CARBON_COPY_COMPLETE; +import static cn.axzo.workflow.common.enums.ProcessMessagePushEventEnum.PROCESS_PUSH_IM; import static cn.axzo.workflow.common.enums.ProcessMessagePushEventEnum.PROCESS_PUSH_NOTICE; import static cn.axzo.workflow.common.enums.ProcessMessagePushEventEnum.PROCESS_PUSH_PENDING; import static cn.axzo.workflow.common.enums.ProcessMessagePushEventEnum.PROCESS_PUSH_PENDING_COMPLETE; @@ -158,7 +161,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId()); if (Objects.nonNull(event.getNoticeConfig().getPending())) { MessagePushDTO dto = build(event.getNoticeConfig().getPending().getPendingMessageId(), - PROCESS_PUSH_PENDING, event, collectionVariable(event)); + PROCESS_PUSH_PENDING, event, collectionVariable(event), event.getActivitySignature()); List buttons = new ArrayList<>(); // 发送待办时, 计算当前人能操作的按钮有哪些? @@ -218,7 +221,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId()); if (Objects.nonNull(event.getNoticeConfig().getPending())) { MessagePushDTO dto = build(event.getNoticeConfig().getPending().getPendingMessageId(), - PROCESS_PUSH_PENDING_COMPLETE, event, collectionVariable(event)); + PROCESS_PUSH_PENDING_COMPLETE, event, collectionVariable(event), event.getActivitySignature()); sendMessageQueue(dto, PROCESS_PUSH_PENDING_COMPLETE); } log.info("RocketMqMessagePushEventListener#onPendingComplete...end, msgTemplateId: {}, receivePerson: {}, processInstanceId: {}", event.getNoticeConfig().getPending().getPendingMessageId(), @@ -234,7 +237,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< return; } log.info("RocketMqMessagePushEventListener#onPendingRollback...start' event: {}", JSONUtil.toJsonStr(event)); - MessagePushDTO dto = build(event.getNoticeConfig().getPending().getPendingMessageId(), PROCESS_PUSH_PENDING_ROLLBACK, event, collectionVariable(event)); + MessagePushDTO dto = build(event.getNoticeConfig().getPending().getPendingMessageId(), PROCESS_PUSH_PENDING_ROLLBACK, event, collectionVariable(event), event.getActivitySignature()); sendMessageQueue(dto, PROCESS_PUSH_PENDING_ROLLBACK); log.info("RocketMqMessagePushEventListener#onPendingRollback...end' event: {}", JSONUtil.toJsonStr(event)); } @@ -251,7 +254,8 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId()); if (Objects.nonNull(event.getNoticeConfig().getCarbonCopy())) { //按人员拆分为多个批次发送消息 - getMessagePushDtoSlice(event, event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(), PROCESS_CARBON_COPY).forEach(dto -> sendMessageQueue(dto, PROCESS_CARBON_COPY)); + getMessagePushDtoSlice(event, event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(), PROCESS_CARBON_COPY) + .forEach(dto -> sendMessageQueue(dto, PROCESS_CARBON_COPY)); } log.info("RocketMqMessagePushEventListener#onCarbonCopy...end, cc' templateId: {}, receivePerson: {}, processInstanceId: {}", event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(), @@ -270,7 +274,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< } List assigners = event.getAssigners(); if (CollectionUtils.isEmpty(assigners) || assigners.size() <= MQ_ASSIGNER_BATCH_SIZE) { - return Collections.singletonList(build(templateId, type, event, collectionVariable(event))); + return Collections.singletonList(build(templateId, type, event, collectionVariable(event), event.getActivitySignature())); } List slice = new ArrayList<>(); Map objectMap = collectionVariable(event); @@ -280,7 +284,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< MessagePushEventImpl messagePushEvent = new MessagePushEventImpl(event.getType()); BeanConverter.convert(event, messagePushEvent); messagePushEvent.setAssigner(batchAssigners); - MessagePushDTO dto = build(templateId, type, messagePushEvent, objectMap); + MessagePushDTO dto = build(templateId, type, messagePushEvent, objectMap, event.getActivitySignature()); slice.add(dto); startIndex = startIndex + MQ_ASSIGNER_BATCH_SIZE; } while (startIndex < assigners.size()); @@ -303,7 +307,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(), event.getProcessInstanceId()); if (Objects.nonNull(event.getNoticeConfig().getCarbonCopy())) { MessagePushDTO dto = build(event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(), - PROCESS_CARBON_COPY_COMPLETE, event, collectionVariable(event)); + PROCESS_CARBON_COPY_COMPLETE, event, collectionVariable(event), event.getActivitySignature()); sendMessageQueue(dto, PROCESS_CARBON_COPY_COMPLETE); } log.info("RocketMqMessagePushEventListener#onCarbonCopyComplete...end, cc' templateId: {}, processInstanceId: {}", @@ -322,13 +326,28 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< event.getNoticeConfig().getSms().getSmsId(), JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId()); if (Objects.nonNull(event.getNoticeConfig().getCarbonCopy())) { MessagePushDTO dto = build(event.getNoticeConfig().getSms().getSmsId(), - PROCESS_PUSH_SMS, event, collectionVariable(event)); + PROCESS_PUSH_SMS, event, collectionVariable(event), event.getActivitySignature()); sendMessageQueue(dto, PROCESS_PUSH_SMS); } log.info("RocketMqMessagePushEventListener#onSms...end, msgTemplateId: {}, receivePerson: {}, processInstanceId: {}", event.getNoticeConfig().getSms().getSmsId(), JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId()); } + @Override + public void onIm(MessagePushEvent event) { + if (!StringUtils.hasText(event.getImTemplateCode()) && !StringUtils.hasText(event.getTerminalType())) { + log.warn("RocketMqMessagePushEventListener#onIm...ignore, imTemplateCode or terminalType is empty, event: {}", JSONUtil.toJsonStr(event)); + return; + } + log.info("RocketMqMessagePushEventListener#onIm...msgTemplateId: {}, receivePerson: {}, processInstanceId: {}", + event.getImTemplateCode(), JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId()); + MessagePushDTO dto = build(event.getImTemplateCode(), + PROCESS_PUSH_IM, event, collectionVariable(event), event.getActivitySignature(), event.getTerminalType()); + sendMessageQueue(dto, PROCESS_PUSH_IM); + log.info("RocketMqMessagePushEventListener#onIm...end, msgTemplateId: {}, receivePerson: {}, processInstanceId: {}", + event.getImTemplateCode(), JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId()); + } + private Map collectionVariable(MessagePushEvent event) { Map variables = new HashMap<>(); @@ -362,6 +381,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< historyService.createHistoricTaskInstanceQuery().taskId(event.getTaskId()).list(); if (!CollectionUtils.isEmpty(tasks)) { // 应该只有 1 个, 但是为了防止有多个, 这里只取第一个 + @SuppressWarnings("unchecked") List assigners = (List) originVariables.getOrDefault(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + tasks.get(0).getTaskDefinitionKey(), Collections.emptyList()); assigners.stream().filter(i -> Objects.equals(i.buildAssigneeId(), tasks.get(0).getAssignee())).findAny() @@ -403,16 +423,24 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< } public MessagePushDTO build(String templateId, ProcessMessagePushEventEnum type, MessagePushEvent event, Map variables) { + Object> variables, Boolean activitySignature) { + return build(templateId, type, event, variables, activitySignature, null); + } + + public MessagePushDTO build(String templateId, ProcessMessagePushEventEnum type, MessagePushEvent event, Map variables, Boolean activitySignature, String terminalType) { return new MessagePushDTO() .setProcessInstanceId(event.getProcessInstanceId()) + .setAdscriptionTenantId(event.getTenantId()) .setProcessDefinitionKey(event.getProcessDefinitionKey()) .setType(type) .setTemplateId(templateId) .setTaskId(event.getTaskId()) .setReceivePersons(event.getAssigners()) .setVariables(variables) - .setProcessApproveConf(event.getProcessApproveConfig()); + .setProcessApproveConf(event.getProcessApproveConfig()) + .setActivitySignature(activitySignature) + .setTerminalType(terminalType); } @@ -422,9 +450,16 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener< } Map header = new HashMap<>(); if (StringUtils.hasText(dto.getProcessDefinitionKey())) { - log.warn("record process definition key: {}", dto.getProcessDefinitionKey()); + log.info("record process definition key: {}", dto.getProcessDefinitionKey()); header.put(MQ_OWNERSHIP_PROCESS_DEFINITION_KEY, dto.getProcessDefinitionKey()); } + if (dto.getVariables().containsKey(PROCESS_OWNERSHIP_APPLICATION)) { + Object orDefault = dto.getVariables().getOrDefault(PROCESS_OWNERSHIP_APPLICATION, ""); + if (Objects.nonNull(orDefault)) { + log.info("record process ownership app name: {}", orDefault); + header.put(MQ_OWNERSHIP_APPLICATION, orDefault.toString()); + } + } eventProducer.send(Event.builder() .shardingKey(dto.getProcessInstanceId()) .eventCode(eventEnum.getEventCode()) diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/FileArchiveProcessEventListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/FileArchiveProcessEventListener.java new file mode 100644 index 000000000..985e7a6f1 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/FileArchiveProcessEventListener.java @@ -0,0 +1,120 @@ +package cn.axzo.workflow.server.controller.listener.process; + +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import cn.axzo.workflow.common.model.dto.VariableObjectDTO; +import cn.axzo.workflow.common.model.request.bpmn.BpmnSignConf; +import cn.axzo.workflow.core.common.context.ProcessOperationContext; +import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; +import cn.axzo.workflow.core.listener.AbstractBpmnEventListener; +import cn.axzo.workflow.core.listener.BpmnProcessEventListener; +import cn.axzo.workflow.core.repository.entity.ExtAxDocContent; +import cn.axzo.workflow.core.repository.entity.ExtAxProcessSign; +import cn.axzo.workflow.core.service.ExtAxDocContentService; +import cn.axzo.workflow.core.service.ExtAxModelDocService; +import cn.axzo.workflow.core.service.ExtAxProcessSignService; +import cn.axzo.workflow.server.common.util.WpsUtil; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.engine.HistoryService; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.impl.util.ProcessDefinitionUtil; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Scope; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 签署业务,审批完成后,进行文件归档,并对所有工人发送业务待办 + * + * @author wangli + * @since 2025-04-01 17:58 + */ +@Slf4j +@Component +@Scope("prototype") +@RefreshScope +public class FileArchiveProcessEventListener extends AbstractBpmnEventListener implements BpmnProcessEventListener, Ordered { + @Resource + private HistoryService historyService; + @Resource + private ExtAxProcessSignService extAxProcessSignService; + @Resource + private ExtAxModelDocService extAxModelDocService; + @Resource + private ExtAxDocContentService extAxDocContentService; + @Resource + private WpsUtil wpsUtil; + + + @Override + public void onCompleted(FlowableEngineEntityEvent event) { + Process mainProcess = ProcessDefinitionUtil.getBpmnModel(event.getProcessDefinitionId()).getMainProcess(); + Optional signConfig = BpmnMetaParserHelper.getSignConfig(mainProcess); + if (!signConfig.isPresent() || Objects.isNull(signConfig.get().getSignType())) { + // 未配置签批设置 + return; + } + + String processInstanceId = event.getProcessInstanceId(); + HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery() + .processInstanceId(processInstanceId).includeProcessVariables().singleResult(); + + // 文件归档,将审批过程中产生的数据全部替换文档模板变量 + archiveFinalDocs(instance); + } + + private void archiveFinalDocs(HistoricProcessInstance instance) { + ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) getEngineConfiguration(); + ExtAxProcessSign processSign = extAxProcessSignService.findByProcessInstanceId(instance.getId()); + List wpsReplaceVariables = wpsUtil.getWpsReplaceVariables(processEngineConfiguration, instance.getId()); + processSign.getFileArchive().stream().filter(i -> Objects.equals(i.getFileType(), FileTypeEnum.WORD) + || Objects.equals(i.getFileType(), FileTypeEnum.EXCEL)) + .forEach(docBaseVO -> { + String newFileKey = wpsUtil.wpsFileVariableReplace(wpsReplaceVariables, null, docBaseVO.getFileKey(), docBaseVO.getTemplateName() + docBaseVO.getFileType().getSuffix()); + docBaseVO.setFileKey(newFileKey); + }); + // 删除非 WPS 的临时文档 + removeTempFile(processSign); + // 暂时不删除 WPS 的临时文档 +// removeTempWpsFile(processSign); + extAxProcessSignService.updateById(processSign); + } + + private void removeTempWpsFile(ExtAxProcessSign processSign) { + processSign.getDocTemplate().stream() + .filter(i -> Objects.equals(i.getFileType(), FileTypeEnum.WORD) || i.getFileType().equals(FileTypeEnum.EXCEL)) + .forEach(docBaseVO -> { + wpsUtil.deleteWpsFile(docBaseVO.getFileCode()); + }); + } + + private void removeTempFile(ExtAxProcessSign processSign) { + List deleteTempFileIds = processSign.getDocTemplate().stream().filter(i -> Objects.equals(i.getFileType(), FileTypeEnum.HIPRINT)).map(SignFileDTO::getFileCode).map(Long::parseLong).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(deleteTempFileIds)) { + return; + } + List contents = extAxDocContentService.getByIds(deleteTempFileIds); + extAxModelDocService.batchDeleteDoc(contents.stream().map(ExtAxDocContent::getFileId).collect(Collectors.toList())); + } + + private ProcessEngineConfiguration getEngineConfiguration() { + return getContext().getProcessEngineConfiguration(CommandContextUtil::getProcessEngineConfiguration); + } + + @Override + public int getOrder() { + return Integer.MIN_VALUE + 3; + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/MessagePushProcessEventListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/MessagePushProcessEventListener.java index 75660f280..75538d500 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/MessagePushProcessEventListener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/MessagePushProcessEventListener.java @@ -76,7 +76,7 @@ public class MessagePushProcessEventListener extends AbstractBpmnEventListener

{ MessagePushEventImpl messagePushEvent = MessagePushEventBuilder.createEvent(MessagePushEventType.PENDING_COMPLETE, null, noticeConfig, - event.getProcessInstanceId(), parseProcessDefinitionKey(event.getProcessDefinitionId()), null, null); + event.getProcessInstanceId(), parseProcessDefinitionKey(event.getProcessDefinitionId()), null, null, false); log.info("发送完成实例下所有待办的消息: {}", JSONUtil.toJsonStr(messagePushEvent)); @@ -87,7 +87,7 @@ public class MessagePushProcessEventListener extends AbstractBpmnEventListener

BpmnTaskDelegateAssigner.toObjectCompatible( + runtimeService.getVariable(event.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, BpmnTaskDelegateAssigner.class)))) .setVariables(((FlowableProcessCancelledEventImpl) event).getExecution().getVariables()) .setStartTime(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getStartTime()) .setTenantId(((FlowableProcessCancelledEventImpl) event).getExecution().getTenantId()) .setBusinessKey(((FlowableProcessCancelledEventImpl) event).getExecution().getProcessInstanceBusinessKey()) .setBusinessProcessInstanceName(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getName()); BpmnMetaParserHelper.getNoticeConfig(mainProcess).ifPresent(dto::setNoticeConf); + BpmnMetaParserHelper.getSignConfig(mainProcess).ifPresent(dto::setSignConf); + setProcessDeleteReason(event, dto); setProcessInstanceVersion(event.getProcessInstanceId(), dto); sendMessageQueue(dto, PROCESS_INSTANCE_CANCELLED); @@ -179,12 +188,16 @@ public class RocketMqBpmnProcessEventListener extends AbstractBpmnEventListener< .setProcessDefinitionKey(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionKey()) .setProcessDefinitionVersion(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionVersion()) .setInitiator(initiator) + .setLastOperationAssigner(getContext().getLastOperationAssigner(() -> BpmnTaskDelegateAssigner.toObjectCompatible( + runtimeService.getVariable(event.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, BpmnTaskDelegateAssigner.class)))) .setVariables(((FlowableProcessCancelledEventImpl) event).getExecution().getVariables()) .setStartTime(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getStartTime()) .setTenantId(((FlowableProcessCancelledEventImpl) event).getExecution().getTenantId()) .setBusinessKey(((FlowableProcessCancelledEventImpl) event).getExecution().getProcessInstanceBusinessKey()) .setBusinessProcessInstanceName(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getName()); BpmnMetaParserHelper.getNoticeConfig(mainProcess).ifPresent(dto::setNoticeConf); + BpmnMetaParserHelper.getSignConfig(mainProcess).ifPresent(dto::setSignConf); + setProcessDeleteReason(event, dto); setProcessInstanceVersion(event.getProcessInstanceId(), dto); sendMessageQueue(dto, PROCESS_INSTANCE_REJECTED); @@ -210,12 +223,16 @@ public class RocketMqBpmnProcessEventListener extends AbstractBpmnEventListener< .setProcessDefinitionKey(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionKey()) .setProcessDefinitionVersion(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getProcessDefinitionVersion()) .setInitiator(initiator) + .setLastOperationAssigner(getContext().getLastOperationAssigner(() -> BpmnTaskDelegateAssigner.toObjectCompatible( + runtimeService.getVariable(event.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, BpmnTaskDelegateAssigner.class)))) .setVariables(((FlowableProcessCancelledEventImpl) event).getExecution().getVariables()) .setStartTime(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getStartTime()) .setTenantId(((FlowableProcessCancelledEventImpl) event).getExecution().getTenantId()) .setBusinessKey(((FlowableProcessCancelledEventImpl) event).getExecution().getProcessInstanceBusinessKey()) .setBusinessProcessInstanceName(((ExecutionEntityImpl) ((FlowableProcessCancelledEventImpl) event).getExecution()).getName()); BpmnMetaParserHelper.getNoticeConfig(mainProcess).ifPresent(dto::setNoticeConf); + BpmnMetaParserHelper.getSignConfig(mainProcess).ifPresent(dto::setSignConf); + setProcessDeleteReason(event, dto); setProcessInstanceVersion(event.getProcessInstanceId(), dto); sendMessageQueue(dto, PROCESS_INSTANCE_ABORTED); @@ -241,12 +258,16 @@ public class RocketMqBpmnProcessEventListener extends AbstractBpmnEventListener< .setProcessDefinitionKey(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionKey()) .setProcessDefinitionVersion(((ExecutionEntityImpl) event.getEntity()).getProcessDefinitionVersion()) .setInitiator(initiator) + .setLastOperationAssigner(getContext().getLastOperationAssigner(() -> BpmnTaskDelegateAssigner.toObjectCompatible( + runtimeService.getVariable(event.getProcessInstanceId(), CLOSE_PROCESS_ASSIGNER, BpmnTaskDelegateAssigner.class)))) .setVariables(((ExecutionEntityImpl) event.getEntity()).getVariables()) .setStartTime(((ExecutionEntityImpl) event.getEntity()).getStartTime()) .setTenantId(((ExecutionEntityImpl) event.getEntity()).getTenantId()) .setBusinessKey(((ExecutionEntityImpl) event.getEntity()).getProcessInstanceBusinessKey()) .setBusinessProcessInstanceName(((ExecutionEntityImpl) event.getEntity()).getName()); BpmnMetaParserHelper.getNoticeConfig(mainProcess).ifPresent(dto::setNoticeConf); + BpmnMetaParserHelper.getSignConfig(mainProcess).ifPresent(dto::setSignConf); + String version = (String) runtimeService.getVariable(event.getProcessInstanceId(), WORKFLOW_ENGINE_VERSION); if (Objects.isNull(version)) { version = FLOW_SERVER_VERSION_121; @@ -277,9 +298,16 @@ public class RocketMqBpmnProcessEventListener extends AbstractBpmnEventListener< } Map header = new HashMap<>(); if (StringUtils.hasText(dto.getProcessDefinitionKey())) { - log.warn("record process definition key: {}", dto.getProcessDefinitionKey()); + log.info("record process definition key: {}", dto.getProcessDefinitionKey()); header.put(MQ_OWNERSHIP_PROCESS_DEFINITION_KEY, dto.getProcessDefinitionKey()); } + if (dto.getVariables().containsKey(PROCESS_OWNERSHIP_APPLICATION)) { + Object orDefault = dto.getVariables().getOrDefault(PROCESS_OWNERSHIP_APPLICATION, ""); + if (Objects.nonNull(orDefault)) { + log.info("record process ownership app name: {}", orDefault); + header.put(MQ_OWNERSHIP_APPLICATION, orDefault.toString()); + } + } eventProducer.send(Event.builder() .shardingKey(dto.getProcessInstanceId()) .eventCode(eventEnum.getEventCode()) diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/SyncToEsProcessEventListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/SyncToEsProcessEventListener.java index 04261797c..49ac875af 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/SyncToEsProcessEventListener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/process/SyncToEsProcessEventListener.java @@ -6,14 +6,12 @@ import cn.axzo.workflow.common.enums.ElasticSearchEventEnum; import cn.axzo.workflow.core.common.context.ProcessOperationContext; import cn.axzo.workflow.core.listener.AbstractBpmnEventListener; import cn.axzo.workflow.core.listener.BpmnProcessEventListener; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.core.Ordered; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java index 8d13d9b22..a165a4771 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java @@ -71,15 +71,19 @@ public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener processApproveConf = BpmnMetaParserHelper.getProcessApproveConf(mainProcess); + // 开启了电子签名不能自动过审 + Boolean activitySignature = BpmnMetaParserHelper.getActivitySignature(mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey())); //自动过审配置连续节点自动过审才处理,历史数据默认不自动过审 - if (processApproveConf.isPresent() && AutoApprovalTypeEnum.CONTINUOUS_NODES_AUTO_APPROVAL == processApproveConf.get().getAutoApprovalType()) { + if (processApproveConf.isPresent() + && AutoApprovalTypeEnum.CONTINUOUS_NODES_AUTO_APPROVAL == processApproveConf.get().getAutoApprovalType() + && !activitySignature) { Object versionVar = delegateTask.getVariable(WORKFLOW_ENGINE_VERSION); String version = versionVar == null ? null : String.valueOf(versionVar); CheckApproverService checkApproverService = MultiVersionBeanUtils.getSpecifiedVersionBean(CheckApproverService.class, version); boolean exists = checkApproverService.checkApproverExists(delegateTask, userTask, mainProcess, getContext()); log.info("是否需要自动过程判断 exists:{},processInstId:{},taskDefinitionKey:{}", exists, delegateTask.getProcessInstanceId(), delegateTask.getTaskDefinitionKey()); if (exists) { - autoPass(delegateTask, "同一审批人,自动过审"); + autoPass(delegateTask, null,"已同意(同一审批人,自动过审)"); } } // 检测节点自身配置是否有自动操作 @@ -132,10 +136,10 @@ public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener { switch (approverEmptyHandleTypeEnum) { case autoPassed: - autoPass(delegateTask); + autoPass(delegateTask, "", "未找到审批人,自动同意"); break; case autoRejection: - autoReject(delegateTask); + autoReject(delegateTask, "未找到审批人,自动驳回"); break; case autoSkipped: // autoReject(delegateTask); @@ -180,9 +184,13 @@ public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener + * 流程实例启用成功后: + * 1、将模型关联的且实例勾选使用过的文档进行原始模板复制 + * 2、进行初始数据的模板变量替换 + *

+ * 并存至 ext_ax_process_sign 中的 doc_template 字段 + * + * @author wangli + * @since 2025-05-16 15:12 + */ +@Slf4j +@Component +@Scope("prototype") +public class FirstCopyTemplateFileTaskEvent_105_Listener extends AbstractBpmnEventListener implements BpmnTaskEventListener, Ordered { + @Resource + private ExtAxProcessSignService extAxProcessSignService; + @Resource + private ExtAxModelDocMapper extAxModelDocMapper; + @Resource + private ExtAxReModelService extAxReModelService; + @Resource + private ExtAxModelDocService extAxModelDocService; + @Resource + private DocAnonymousDatabaseApi docAnonymousApi; + @Resource + private WpsUtil wpsUtil; + + @Override + public void onCompleted(DelegateTask delegateTask) { + if (!Objects.equals(NODE_STARTER.getType(), delegateTask.getTaskDefinitionKey())) { + return; + } + String processInstanceId = delegateTask.getProcessInstanceId(); + + Process mainProcess = ProcessDefinitionUtil.getBpmnModel(delegateTask.getProcessDefinitionId()).getMainProcess(); + Optional signConfig = BpmnMetaParserHelper.getSignConfig(mainProcess); + if (!signConfig.isPresent() || Objects.isNull(signConfig.get().getSignType())) { + return; + } + + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + List docs = commandExecutor.execute(new CustomGetModelDocsCmd(processInstanceId, true, extAxModelDocMapper, extAxReModelService)); + ExtAxProcessSign processSign = new ExtAxProcessSign(); + processSign.setProcessInstanceId(processInstanceId); + processSign.setSignType(signConfig.get().getSignType().getType()); + processSign.setPendingMessageId(signConfig.get().getSignPendingProperty().getPendingMessageId()); + if (CollectionUtils.isEmpty(docs)) { + processSign.setDocTemplate(Collections.emptyList()); + processSign.setFileArchive(Collections.emptyList()); + } else { + // 复制基础模板 + List docTemplates = copyTempTemplate(docs); + processSign.setDocTemplate(docTemplates); + + List archives = replaceTemplateVariable(docTemplates, processEngineConfiguration, processInstanceId); + processSign.setFileArchive(archives); + } + // 没有可用的文档,但仍然记录库表 + extAxProcessSignService.save(processSign); + } + + private List replaceTemplateVariable(List docTemplates, ProcessEngineConfigurationImpl processEngineConfiguration, String processInstanceId) { + List archives = new ArrayList<>(); + List wpsReplaceVariables = wpsUtil.getWpsReplaceVariables(processEngineConfiguration, processInstanceId); + docTemplates.forEach(template -> { + SignFileDTO signFileDTO = new SignFileDTO(); + signFileDTO.setId(template.getId()); + signFileDTO.setFileName(template.getFileName()); + signFileDTO.setTemplateName(template.getTemplateName()); + signFileDTO.setFileTag(template.getFileTag()); + signFileDTO.setFileType(template.getFileType()); + signFileDTO.setFileCode(template.getFileCode()); + if (Objects.equals(template.getFileType(), FileTypeEnum.WORD) || Objects.equals(template.getFileType(), FileTypeEnum.EXCEL)) { + String fileKey = wpsUtil.wpsFileVariableReplace(wpsReplaceVariables, template.getFileCode(), null, template.getTemplateName() + template.getFileType().getSuffix()); + signFileDTO.setFileKey(fileKey); + } + archives.add(signFileDTO); + }); + return archives; + } + + private List copyTempTemplate(List docs) { + List files = new ArrayList<>(); + docs.forEach(doc -> { + SignFileDTO signFileDTO = new SignFileDTO(); + // 原始文档 ID + signFileDTO.setId(doc.getId()); + signFileDTO.setFileName(doc.getFileName()); + signFileDTO.setTemplateName(doc.getTemplateName()); + signFileDTO.setFileTag(doc.getTag()); + signFileDTO.setFileType(doc.getFileType()); + switch (doc.getFileType()) { + case WORD: + case EXCEL: + CopyNodeRequest copy = new CopyNodeRequest(); + copy.setCode(doc.getFileRelationId()); + String fileCode = RpcExternalUtil.rpcProcessor(() -> docAnonymousApi.copy(copy), "复制文档", copy); + signFileDTO.setFileCode(fileCode); + break; + case HIPRINT: + Long newDocId = extAxModelDocService.cloneDoc(DocCloneDTO.builder().docId(doc.getId()).build(), true); + signFileDTO.setFileCode(String.valueOf(newDocId)); + break; + case PDF: + break; + default: + break; + } + files.add(signFileDTO); + }); + return files; + } + + @Override + public int getOrder() { + return Integer.MIN_VALUE + 105; + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/MessagePushTaskEvent_103_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/MessagePushTaskEvent_103_Listener.java index 066979cfe..d6243acec 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/MessagePushTaskEvent_103_Listener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/MessagePushTaskEvent_103_Listener.java @@ -38,7 +38,7 @@ import java.util.Optional; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT; import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT; -import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER; +import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getActivitySignature; import static cn.axzo.workflow.core.engine.event.BizSpecifyAssigneeEventType.ADD_ASSIGNEE; /** @@ -65,9 +65,9 @@ public class MessagePushTaskEvent_103_Listener extends AbstractBpmnEventListener public void onAssigned(DelegateTask delegateTask) { log.info("MessagePushTaskEventListener#onAssigned...{}, assignee: {}, taskId: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getAssignee(), delegateTask.getId(), delegateTask.getProcessInstanceId()); - if (Objects.equals(NODE_STARTER.getType(), delegateTask.getTaskDefinitionKey())) { - return; - } +// if (Objects.equals(NODE_STARTER.getType(), delegateTask.getTaskDefinitionKey())) { +// return; +// } if (StringUtils.hasLength(delegateTask.getAssignee()) && delegateTask.getAssignee().contains(TASK_ASSIGNEE_SKIP_FLAT)) { // 转交功能原审批人完成待办, 由于在流程引擎侧, 任务是不会在转交时立即结束, 但待办消息需要立即完成, // 下面的 onDelete 事件根据测试情况,看是否需要过滤掉这种任务的"完成待办"事件的推送 @@ -95,9 +95,9 @@ public class MessagePushTaskEvent_103_Listener extends AbstractBpmnEventListener @Override public void onDeleted(DelegateTask delegateTask) { log.info("MessagePushTaskEventListener#onDeleted...{}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); - if (Objects.equals(NODE_STARTER.getType(), delegateTask.getTaskDefinitionKey())) { - return; - } +// if (Objects.equals(NODE_STARTER.getType(), delegateTask.getTaskDefinitionKey())) { +// return; +// } pendingCompleteEvent(delegateTask); log.info("MessagePushTaskEventListener#onDeleted...end: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); } @@ -115,7 +115,8 @@ public class MessagePushTaskEvent_103_Listener extends AbstractBpmnEventListener MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.PENDING_COMPLETE, null, noticeConfig, delegateTask.getProcessInstanceId(), - parseProcessDefinitionKey(delegateTask.getProcessDefinitionId()), null, delegateTask.getId()); + parseProcessDefinitionKey(delegateTask.getProcessDefinitionId()), + null, delegateTask.getId(), getActivitySignature(userTask)); log.info("发送完成待办的消息: {}, processInstanceId:{}", JSONUtil.toJsonStr(event), delegateTask.getProcessInstanceId()); eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey()); }); @@ -126,6 +127,7 @@ public class MessagePushTaskEvent_103_Listener extends AbstractBpmnEventListener .processInstanceId(delegateTask.getProcessInstanceId()) .includeProcessVariables() .singleResult()); + @SuppressWarnings("unchecked") List assigners = (List) processInstance.getProcessVariables() .getOrDefault(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + delegateTask.getTaskDefinitionKey(), Collections.emptyList()); @@ -143,7 +145,6 @@ public class MessagePushTaskEvent_103_Listener extends AbstractBpmnEventListener // 优先读取节点自身的待办模板配置信息 UserTask userTask = (UserTask) process.getFlowElement(delegateTask.getTaskDefinitionKey()); BpmnMetaParserHelper.getNodePendingConfig(userTask).ifPresent(noticeConf::setPending); - assigners.stream().filter(i -> Objects.equals(delegateTask.getAssignee(), i.buildAssigneeId())).findAny() .ifPresent(i -> { MessagePushEventImpl event = @@ -153,7 +154,8 @@ public class MessagePushTaskEvent_103_Listener extends AbstractBpmnEventListener processInstance.getProcessInstanceId(), processInstance.getProcessDefinitionId(), processInstance.getProcessDefinitionKey(), userTask.getId(), - processInstance.getTenantId(), delegateTask.getId()); + processInstance.getTenantId(), delegateTask.getId(), + getActivitySignature(userTask)); log.info("发送推送待办的消息: {}, processInstanceId:{}", JSONUtil.toJsonStr(event), event.getProcessInstanceId()); eventDispatcher.dispatchEvent(event, processEngineConfiguration.getEngineCfgKey()); }); diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/RocketMqBpmnTaskEvent_102_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/RocketMqBpmnTaskEvent_102_Listener.java index c1c1acf0c..ff1e2b666 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/RocketMqBpmnTaskEvent_102_Listener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/RocketMqBpmnTaskEvent_102_Listener.java @@ -35,14 +35,20 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR; import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO; +import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_APPLICATION; import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_PROCESS_DEFINITION_KEY; +import static cn.axzo.workflow.common.constant.BpmnConstants.PROCESS_OWNERSHIP_APPLICATION; +import static cn.axzo.workflow.common.constant.BpmnConstants.SKIP_MQ; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ASSIGNEE_SKIP_FLAT; import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_ATTACHMENTS_VAR_NAME; +import static cn.axzo.workflow.common.constant.BpmnConstants.TRANSFER_TO; +import static cn.axzo.workflow.common.constant.BpmnConstants.TRANSFER_TO_ADVICE; import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION; import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_ASSIGNED; import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_COMPLETED; import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_CREATED; import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_DELETED; +import static cn.axzo.workflow.common.enums.ProcessTaskEventEnum.PROCESS_TASK_TRANSFER; /** @@ -95,6 +101,11 @@ public class RocketMqBpmnTaskEvent_102_Listener extends AbstractBpmnEventListene @Override public void onCompleted(DelegateTask delegateTask) { log.info("RocketMqBpmnTaskEventListener#onCompleted...{}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); + Object skipMq = delegateTask.getTransientVariable(SKIP_MQ); + if (Objects.nonNull(skipMq) && Boolean.TRUE.equals(skipMq)) { + // reject 使用了虚拟任务,会触发完成 + return; + } ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_COMPLETED); sendMessageQueue(dto, PROCESS_TASK_COMPLETED); log.info("RocketMqBpmnTaskEventListener#onCompleted...end: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); @@ -108,6 +119,18 @@ public class RocketMqBpmnTaskEvent_102_Listener extends AbstractBpmnEventListene log.info("RocketMqBpmnTaskEventListener#onDeleted...end: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); } + @Override + public void onTransfer(DelegateTask delegateTask) { + log.info("RocketMqBpmnTaskEventListener#onTransfer...{}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); + BpmnTaskDelegateAssigner assigner = (BpmnTaskDelegateAssigner) delegateTask.getTransientVariable(TRANSFER_TO); + String transferAdvice = (String) delegateTask.getTransientVariable(TRANSFER_TO_ADVICE); + ProcessTaskDTO dto = build(delegateTask, PROCESS_TASK_TRANSFER); + dto.setTransferTargetApprover(assigner); + dto.setAdvice(transferAdvice); + sendMessageQueue(dto, PROCESS_TASK_TRANSFER); + log.info("RocketMqBpmnTaskEventListener#onTransfer...end: {}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); + } + private Deployment getDeployment(String processInstanceId) { ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); @@ -157,10 +180,17 @@ public class RocketMqBpmnTaskEvent_102_Listener extends AbstractBpmnEventListene Map header = new HashMap<>(); if (StringUtils.hasText(dto.getProcessDefinitionKey())) { if (log.isDebugEnabled()) { - log.debug("record process definition key: {}", dto.getProcessDefinitionKey()); + log.info("record process definition key: {}", dto.getProcessDefinitionKey()); } header.put(MQ_OWNERSHIP_PROCESS_DEFINITION_KEY, dto.getProcessDefinitionKey()); } + if (dto.getVariables().containsKey(PROCESS_OWNERSHIP_APPLICATION)) { + Object orDefault = dto.getVariables().getOrDefault(PROCESS_OWNERSHIP_APPLICATION, ""); + if (Objects.nonNull(orDefault)) { + log.info("record process ownership app name: {}", orDefault); + header.put(MQ_OWNERSHIP_APPLICATION, orDefault.toString()); + } + } eventProducer.send(Event.builder() .shardingKey(dto.getProcessInstanceId()) .eventCode(eventEnum.getEventCode()) diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SnapshotBpmnTaskEvent_100_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SnapshotBpmnTaskEvent_100_Listener.java index 8e86bb6a9..43d072d33 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SnapshotBpmnTaskEvent_100_Listener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SnapshotBpmnTaskEvent_100_Listener.java @@ -46,6 +46,7 @@ public class SnapshotBpmnTaskEvent_100_Listener extends AbstractBpmnEventListene @Override public void onAssigned(DelegateTask delegateTask) { log.info("SnapshotBpmnTaskTaskEventListener#onAssigned...{}, processInstanceId:{}", delegateTask.getTaskDefinitionKey(), delegateTask.getProcessInstanceId()); + @SuppressWarnings("unchecked") List assignerList = runtimeService.getVariable(delegateTask.getProcessInstanceId(), INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + delegateTask.getTaskDefinitionKey(), List.class); diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SyncToEsTaskEvent_104_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SyncToEsTaskEvent_104_Listener.java index c8f49d18c..377a5fdf3 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SyncToEsTaskEvent_104_Listener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/SyncToEsTaskEvent_104_Listener.java @@ -11,7 +11,6 @@ import org.flowable.task.service.delegate.DelegateTask; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.core.Ordered; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/TestController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/TestController.java index 456881e73..8f4a09a4f 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/TestController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/TestController.java @@ -15,6 +15,8 @@ import cn.axzo.workflow.core.service.support.FlowNodeForecastService; import cn.axzo.workflow.form.service.FormDefinitionService; import cn.axzo.workflow.server.common.annotation.RepeatSubmit; import cn.axzo.workflow.server.common.util.ShellUtil; +import cn.axzo.workflow.server.xxljob.EsIndexOperationJobHandler; +import cn.axzo.workflow.server.xxljob.SpecifyProcessInstanceSyncEsJobHandler; import cn.azxo.framework.common.model.CommonResponse; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.FlowElement; @@ -33,11 +35,8 @@ import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.PropertyPlaceholderHelper; import org.springframework.util.StringUtils; -import org.springframework.util.StringValueResolver; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -99,6 +98,10 @@ public class TestController { // private WorkflowManageService workflowManageService; @Resource private SpringProcessEngineConfiguration processEngineConfiguration; + @Resource + private EsIndexOperationJobHandler esIndexOperationJobHandler; + @Resource + private SpecifyProcessInstanceSyncEsJobHandler specifyProcessInstanceSyncEsJobHandler; @RepeatSubmit @GetMapping("/test") @@ -353,5 +356,17 @@ public class TestController { String value = propertyPlaceholderHelper.replacePlaceholders(expression, resolver); return CommonResponse.success(value); } + + @GetMapping("/es/index") + public CommonResponse esIndex(@RequestParam String str) { + esIndexOperationJobHandler.execute(str); + return CommonResponse.success(); + } + + @GetMapping("/es/sync") + public CommonResponse syncProcessInstanceToEs(@RequestParam String processInstanceId) { + specifyProcessInstanceSyncEsJobHandler.execute(processInstanceId); + return CommonResponse.success(); + } } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessDefinitionController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessDefinitionController.java index 1af98818f..7b15bcfd5 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessDefinitionController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessDefinitionController.java @@ -130,11 +130,10 @@ public class BpmnProcessDefinitionController implements ProcessDefinitionApi { @Operation(summary = "获得流程定义标识对应的激活的流程定义") @GetMapping("/active/getByKey") @Override - public CommonResponse getActiveProcessDefinitionByKey(@NotBlank(message = - "模型定义KEY" + - "不能为空") @RequestParam String key) { + public CommonResponse getActiveProcessDefinitionByKey(@NotBlank(message = "模型定义KEY不能为空") @RequestParam String key, + @RequestParam(required = false, defaultValue = NO_TENANT_ID) String tenantId) { log.info("获得流程定义标识对应的激活的流程定义 getActiveProcessDefinitionByKey===>>>参数:{}", JSONUtil.toJsonStr(key)); - return success(bpmnProcessDefinitionService.getActiveProcessDefinitionByKey(key, key)); + return success(bpmnProcessDefinitionService.getActiveProcessDefinitionByKey(key, tenantId)); } /** @@ -216,6 +215,7 @@ public class BpmnProcessDefinitionController implements ProcessDefinitionApi { updateDTO.setTenantId(modelDetail.getTenantId()); updateDTO.setJsonModel(processDefinition.getJsonModel()); + @SuppressWarnings("unchecked") Map metaInfoMap = JSON.parseObject(modelDetail.getMetaInfo(), Map.class); updateDTO.setDescription(metaInfoMap.get(MODEL_DESCRIPTION)); diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessInstanceController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessInstanceController.java index cf0e861a7..4d8bb8002 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessInstanceController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessInstanceController.java @@ -1,33 +1,52 @@ package cn.axzo.workflow.server.controller.web.bpmn; +import cn.axzo.nanopart.doc.api.anonymous.DocAnonymousDatabaseApi; import cn.axzo.oss.http.api.ServerFileServiceApi; import cn.axzo.oss.http.model.ApiSignUrlDownloadRequest; import cn.axzo.oss.http.model.ApiSignUrlDownloadResponse; import cn.axzo.workflow.client.feign.bpmn.ProcessInstanceApi; -import cn.axzo.workflow.common.enums.AttachmentTypeEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO; 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.BpmnProcessInstanceLogQueryDTO; 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.process.HistoricProcessInstanceSearchDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.SuperBpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskButtonSearchDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; 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.model.doc.DocBaseVO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO; 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.HistoricProcessInstanceVO; +import cn.axzo.workflow.common.model.response.bpmn.process.NodesByModelVO; import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; +import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO; +import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskButtonVo; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceLogVO; +import cn.axzo.workflow.common.valid.group.ValidGroup; +import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd; +import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper; import cn.axzo.workflow.core.service.BpmnProcessInstanceService; +import cn.axzo.workflow.core.service.BpmnProcessTaskService; +import cn.axzo.workflow.core.service.ExtAxProcessSignService; +import cn.axzo.workflow.core.service.ExtAxReModelService; +import cn.axzo.workflow.core.service.ExtAxReadRecordService; import cn.axzo.workflow.server.common.annotation.ErrorReporter; import cn.axzo.workflow.server.common.annotation.RepeatSubmit; import cn.axzo.workflow.server.common.util.RpcExternalUtil; @@ -38,8 +57,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.ListUtils; -import org.apache.commons.lang.math.NumberUtils; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.beans.BeanUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; @@ -56,12 +77,15 @@ import javax.annotation.Nullable; import javax.annotation.Resource; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_ID_NOT_IN_MODEL; +import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_DOC_READ_PARAM_ERROR; import static cn.azxo.framework.common.model.CommonResponse.success; /** @@ -77,7 +101,21 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController @Resource private BpmnProcessInstanceService bpmnProcessInstanceService; @Resource + private BpmnProcessTaskService bpmnProcessTaskService; + @Resource private ServerFileServiceApi serverFileServiceApi; + @Resource + private DocAnonymousDatabaseApi docAnonymousApi; + @Resource + private ExtAxReadRecordService readRecordService; + @Resource + private ExtAxModelDocMapper extAxModelDocMapper; + @Resource + private SpringProcessEngineConfiguration processEngineConfiguration; + @Resource + private ExtAxProcessSignService extAxProcessSignService; + @Resource + private ExtAxReModelService extAxReModelService; /** * 超管查询所有流程实例 @@ -104,6 +142,21 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController return success(bpmnProcessInstanceService.getMyProcessInstancePage(dto)); } + /** + * 创建审批实例 + */ + @Operation(summary = "创建审批流程前,返回模型节点列表以及节点能否设置审批人") + @PostMapping("/create/before") + @Override + @RepeatSubmit + public CommonResponse> nodesBeforeCreateProcessInstance(@Validated @RequestBody BeforeProcessInstanceCreateDTO dto) { + log.info("发起审核前,节点发起人自选nodesBeforeCreateProcessInstance===>>>参数:{}", JSONUtil.toJsonStr(dto)); + List nodes = bpmnProcessInstanceService.nodesBeforeCreateProcessInstance(dto); + // 填充头像 + populateUsersAvatar(nodes.stream().flatMap(e -> e.getAssigners().stream().filter(Objects::nonNull)).collect(Collectors.toList())); + return success(nodes); + } + /** * 创建审批实例 */ @@ -113,8 +166,14 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController @RepeatSubmit public CommonResponse createProcessInstance(@Validated @RequestBody BpmnProcessInstanceCreateDTO dto) { log.info("发起审核createProcessInstance===>>>参数:{}", JSONUtil.toJsonStr(dto)); - // 填充头像 + // 填充名称头像 populateUsersAvatar(dto.getInitiator()); + // 填充指定审批人名称头像 + if (!CollectionUtils.isEmpty(dto.getSpecifyAssignerMap())) { + populateUsersAvatar(dto.getSpecifyAssignerMap().entrySet().stream().flatMap(e -> e.getValue().stream().filter(Objects::nonNull)).collect(Collectors.toList())); + } + // 填充签署人名称头像 + populateUsersAvatar(dto.getSignatories()); dto.setAsync(false); return success(bpmnProcessInstanceService.createProcessInstance(dto)); } @@ -129,9 +188,21 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController @DeleteMapping("/cancel") @Override @RepeatSubmit - public CommonResponse cancelProcessInstance(@Validated @RequestBody BpmnProcessInstanceCancelDTO dto) { + public CommonResponse cancelProcessInstance(@Validated(ValidGroup.Update.class) @RequestBody BpmnProcessInstanceCancelDTO dto) { log.info("撤回审核cancelProcessInstant===>>>参数:{}", JSONUtil.toJsonStr(dto)); populateUsersAvatar(dto.getInitiator()); + SuperBpmnProcessInstanceCancelDTO target = new SuperBpmnProcessInstanceCancelDTO(); + BeanUtils.copyProperties(dto, target); + return success(bpmnProcessInstanceService.cancelProcessInstance(target)); + } + + @Operation(summary = "撤回流程实例(Super)") + @DeleteMapping("/super/cancel") + @RepeatSubmit + @Override + public CommonResponse superCancelProcessInstance(@Validated(ValidGroup.Insert.class) @RequestBody SuperBpmnProcessInstanceCancelDTO dto) { + log.info("超级撤回审核 superCancelProcessInstant===>>>参数:{}", JSONUtil.toJsonStr(dto)); + populateUsersAvatar(dto.getInitiator()); return success(bpmnProcessInstanceService.cancelProcessInstance(dto)); } @@ -282,7 +353,7 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController public CommonResponse> getProcessVariables(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam String processInstanceId, @Nullable String tenantId) { HistoricProcessInstance processInstance = bpmnProcessInstanceService.getProcessInstance(processInstanceId, - tenantId, true); + tenantId, true); return success(processInstance.getProcessVariables()); } @@ -339,8 +410,27 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController return success(log); } + + /** + * 根据任务id查询任务状态,按钮详情 + * + * @param taskButtonsSearchDTO 请求参数 + * @return + */ + @Override + @Operation(summary = "根据流程实例id,任务id查询任务状态,按钮详情") + @PostMapping("/task/buttons/find") + public CommonResponse findProcessSingleTaskButtons(@Validated @RequestBody BpmnTaskButtonSearchDTO taskButtonsSearchDTO) { + return success(bpmnProcessInstanceService.findTaskButtons(taskButtonsSearchDTO)); + } + private void resetPersonInfo(BpmnProcessInstanceLogVO log) { - List users = log.getTaskDetails().stream().map(BpmnTaskInstanceLogVO::getAssigneeSnapshot).filter(Objects::nonNull).filter(e -> NumberUtils.isNumber(e.getPersonId())).collect(Collectors.toList()); + List users = log.getTaskDetails().stream().flatMap(taskLog -> { + Stream assigner = Objects.nonNull(taskLog.getAssigneeSnapshot()) ? Stream.of(taskLog.getAssigneeSnapshot()) : Stream.empty(); + Stream forecastAssignees = CollectionUtils.isEmpty(taskLog.getForecastAssignees()) ? Stream.empty() : taskLog.getForecastAssignees().stream(); + return Stream.concat(assigner, forecastAssignees); + }).collect(Collectors.toList()); +// List users = log.getTaskDetails().stream().map(BpmnTaskInstanceLogVO::getAssigneeSnapshot).filter(Objects::nonNull).filter(e -> NumberUtils.isNumber(e.getPersonId())).collect(Collectors.toList()); users.add(log.getInitiator()); populateUsersAvatar(users); // users.stream().collect(Collectors.toMap(BpmnTaskDelegateAssigner::getPersonId, Function.identity(), (s,t)->s)) @@ -350,17 +440,17 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController private void parseSignatureUrl(BpmnProcessInstanceLogVO log) { List signUrls = log.getTaskDetails().stream() - .filter(i -> StringUtils.hasText(i.getTaskId())) - .map(BpmnTaskInstanceLogVO::getSignatureUrl) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); + .filter(i -> StringUtils.hasText(i.getTaskId())) + .map(BpmnTaskInstanceLogVO::getSignatureUrl) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); if (!CollectionUtils.isEmpty(signUrls)) { Map ossUrlMap = ListUtils.emptyIfNull(getSignPrivateUrl(signUrls)).stream() - .collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, ApiSignUrlDownloadResponse::getSignUrl, (s, t) -> s)); + .collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, ApiSignUrlDownloadResponse::getSignUrl, (s, t) -> s)); log.getTaskDetails().stream() - .filter(i -> StringUtils.hasText(i.getTaskId())) - .forEach(i -> i.setSignatureUrl(ossUrlMap.getOrDefault(i.getSignatureUrl(), null))); + .filter(i -> StringUtils.hasText(i.getTaskId())) + .forEach(i -> i.setSignatureUrl(ossUrlMap.getOrDefault(i.getSignatureUrl(), null))); } } @@ -369,4 +459,72 @@ public class BpmnProcessInstanceController extends BasicPopulateAvatarController request.setFileKeys(signUrls); return RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(request), "批量获取手写签私有访问地址", request); } + + @Operation(summary = "更新指定流程表单最后一次编辑内容中指定 KEY 的全部数据") + @PostMapping("/form/variable/update") + @Override + public CommonResponse updateInstanceFormVariables(@Validated @RequestBody FormVariablesUpdateDTO dto) { + bpmnProcessInstanceService.overrideFormVariables(dto); + return CommonResponse.success(true); + } + + @Override + @Operation(summary = "签署业务流程实例在审批待办中查询使用的文档列表") + @PostMapping("/select/doc/list") + public CommonResponse> processInstanceSelectDocs(@Validated @RequestBody ProcessDocQueryDTO dto) { + List docs = bpmnProcessInstanceService.processInstanceSelectDocs(dto); + if (CollectionUtils.isEmpty(docs)) { + return CommonResponse.success(docs); + } + List fileKeys = docs.stream().map(DocPendingVO::getFileKey).filter(StringUtils::hasText).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(fileKeys)) { + return CommonResponse.success(docs); + } + ApiSignUrlDownloadRequest ossRequest = new ApiSignUrlDownloadRequest(); + ossRequest.setFileKeys(fileKeys); + Map ossUrlMap = RpcExternalUtil.rpcProcessor(() -> serverFileServiceApi.signUrlFetchDownload(ossRequest), "批量获取文件 OSS 地址", ossRequest) + .stream().collect(Collectors.toMap(ApiSignUrlDownloadResponse::getFileKey, Function.identity(), (s, t) -> s)); + docs.forEach(i -> { + if (StringUtils.hasText(i.getFileKey())) { + ApiSignUrlDownloadResponse ossFileInfo = ossUrlMap.getOrDefault(i.getFileKey(), null); + if (Objects.nonNull(ossFileInfo)) { + i.setOssUrl(ossFileInfo.getSignUrl()); + i.setStorageSize(ossFileInfo.getStorageSize()); + } + } + }); + return success(docs); + } + + @Override + @Operation(summary = "获取审批人阅读状态") + @PostMapping("/approver/read/status") + public CommonResponse> approverReadStatus(@Validated @RequestBody ApproverReadStatusDTO dto) { + if (!StringUtils.hasText(dto.getAssigner().getPersonId())) { + throw new WorkflowEngineException(PROCESS_DOC_READ_PARAM_ERROR); + } + return success(readRecordService.queryReadStatus(dto)); + } + + @Override + @Operation(summary = "修改审批人关联文档阅读状态") + @PostMapping("/approver/read/status/change") + public CommonResponse approveReadStatusChange(@Validated @RequestBody ChangeApproverReadStatusDTO dto) { + if (!StringUtils.hasText(dto.getAssigner().getPersonId())) { + throw new WorkflowEngineException(PROCESS_DOC_READ_PARAM_ERROR); + } + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + List relationDocs = commandExecutor.execute(new CustomGetModelDocsCmd(dto.getProcessInstanceId(), true, extAxModelDocMapper, extAxReModelService)); + relationDocs.stream().filter(i -> Objects.equals(i.getId(), dto.getDocId())) + .findAny().orElseThrow(() -> new WorkflowEngineException(PROCESS_DOC_ID_NOT_IN_MODEL)); + + return success(readRecordService.changeReadStatus(dto)); + } + + @Override + @Operation(summary = "获取签署业务流程最后替换的文档 fileKey 集合") + @GetMapping("/final/docs") + public CommonResponse> getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId) { + return success(extAxProcessSignService.findByProcessInstanceId(processInstanceId).getFileArchive()); + } } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessModelController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessModelController.java index 9fbc481cb..d1d6b13d6 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessModelController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessModelController.java @@ -1,24 +1,72 @@ package cn.axzo.workflow.server.controller.web.bpmn; +import cn.axzo.basics.common.BeanMapper; +import cn.axzo.nanopart.doc.api.anonymous.DocAnonymousDatabaseApi; +import cn.axzo.nanopart.doc.api.index.request.CopyNodeRequest; +import cn.axzo.oss.http.api.ServerFileServiceApi; +import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectRequest; +import cn.axzo.oss.http.model.copyobject.ServerFileBatchCopyObjectResponse; import cn.axzo.workflow.client.feign.bpmn.ProcessModelApi; +import cn.axzo.workflow.common.enums.FileTypeEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; 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; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocByIdDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCloneDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCreateDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocOrderDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocResetDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocTenantQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocUpdateDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.RestPrintTemplateConfigDTO; import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelDetailVO; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; +import cn.axzo.workflow.common.model.response.print.PrintModelDTO; +import cn.axzo.workflow.core.conf.CustomEventManager; +import cn.axzo.workflow.core.engine.cmd.CustomGetModelByDefinitionIdCmd; +import cn.axzo.workflow.core.engine.cmd.CustomGetModelDocsCmd; +import cn.axzo.workflow.core.engine.event.DocChangeEventImpl; +import cn.axzo.workflow.core.repository.entity.ExtAxModelDoc; +import cn.axzo.workflow.core.repository.mapper.ExtAxModelDocMapper; import cn.axzo.workflow.core.service.AggregateModelService; +import cn.axzo.workflow.core.service.BpmnProcessDefinitionService; +import cn.axzo.workflow.core.service.BpmnProcessInstanceService; import cn.axzo.workflow.core.service.BpmnProcessModelService; +import cn.axzo.workflow.core.service.ExtAxDocContentService; +import cn.axzo.workflow.core.service.ExtAxModelDocService; import cn.axzo.workflow.core.service.ExtAxReModelService; import cn.axzo.workflow.server.common.annotation.ErrorReporter; import cn.axzo.workflow.server.common.annotation.RepeatSubmit; +import cn.axzo.workflow.server.common.util.WpsUtil; import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.core.util.NumberUtil; import cn.hutool.json.JSONUtil; import com.alibaba.fastjson.JSON; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.ListUtils; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.engine.repository.Model; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -32,9 +80,19 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_CLONE_ERROR; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_HIPRINT_ID_INVAILD; +import static cn.axzo.workflow.common.code.BpmnModelRespCode.MODEL_FILE_QUERY_ERROR; +import static cn.axzo.workflow.common.code.OtherRespCode.ILLEGAL_PARAM_ERROR; +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; +import static cn.axzo.workflow.common.enums.FileTypeEnum.EXCEL; +import static cn.axzo.workflow.common.enums.FileTypeEnum.WORD; import static cn.azxo.framework.common.model.CommonResponse.success; /** @@ -50,9 +108,29 @@ public class BpmnProcessModelController implements ProcessModelApi { @Resource private BpmnProcessModelService bpmnProcessModelService; @Resource + private BpmnProcessDefinitionService bpmnProcessDefinitionService; + @Resource private AggregateModelService aggregateModelService; @Resource private ExtAxReModelService reModelService; + @Resource + private BpmnProcessInstanceService bpmnProcessInstanceService; + @Resource + private ExtAxModelDocService modelDocService; + @Resource + private ExtAxDocContentService docContentService; + @Resource + private DocAnonymousDatabaseApi docAnonymousApi; + @Resource + private ServerFileServiceApi fileServiceApi; + @Resource + private SpringProcessEngineConfiguration springProcessEngineConfiguration; + @Resource + private ExtAxModelDocMapper extAxModelDocMapper; + @Resource + private WpsUtil wpsUtil; + @Resource + private CustomEventManager eventPublisher; /** * 获取流程模型的查询结果 @@ -83,6 +161,20 @@ public class BpmnProcessModelController implements ProcessModelApi { } else { bpmnModelId = aggregateModelService.createBpmnAndFormModel(dto); } + + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + List commonDocBaseVos = commandExecutor.execute(new CustomGetModelDocsCmd(null, dto.getKey(), null, false, false, extAxModelDocMapper, reModelService)); + //通用模板的文档 + if (!CollectionUtils.isEmpty(commonDocBaseVos)) { + //进行克隆 + commonDocBaseVos.forEach(cb -> this.singleCloneDoc(cb.getId(), DocModelInfo.builder() + .targetModelKey(dto.getKey()) + .targetModelId(bpmnModelId) + .tenantId(dto.getTenantId()) + .tag(cb.getTag()) + .build())); + eventPublisher.publishEvent(new DocChangeEventImpl(dto.getKey(), dto.getTenantId(), Collections.emptyList(), modelDocService.querySetting(dto.getKey(), NO_TENANT_ID))); + } return success(bpmnModelId); } @@ -146,7 +238,7 @@ public class BpmnProcessModelController implements ProcessModelApi { @Override public CommonResponse update(@Validated @RequestBody BpmnModelUpdateDTO dto) { log.info("修改流程信息updateBpmModel===>>> 模型 ID: {}", dto.getProcessModelId()); - if(Objects.isNull(dto.getFormJsonModel())) { + if (Objects.isNull(dto.getFormJsonModel())) { bpmnProcessModelService.updateBpmModel(dto); } else { aggregateModelService.updateBpmnAndFormModel(dto); @@ -258,14 +350,40 @@ public class BpmnProcessModelController implements ProcessModelApi { @PostMapping("/changeStatus") @Override public CommonResponse changeStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, - @NotNull(message = "状态不能为空") @RequestParam Integer status, - @RequestParam(required = false) String operator) { + @NotNull(message = "状态不能为空") @RequestParam Integer status, + @RequestParam(required = false) String operator) { log.info("修改模型状态changeStatus===>>>参数:{}", JSONUtil.toJsonStr(modelId)); - BpmnTaskDelegateAssigner assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class); + BpmnTaskDelegateAssigner assignee = null; + if (StringUtils.hasText(operator)) { + assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class); + } bpmnProcessModelService.changeStatus(modelId, status, assignee); return success(true); } + /** + * 修改模型打印开关状态 + * + * @param modelId + * @param status + * @param operator + * @return + */ + @Operation(summary = "修改模型打印开关状态") + @PostMapping("/print/changeStatus") + @Override + public CommonResponse changePrintStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, + @NotNull(message = "状态不能为空") @RequestParam Integer status, + @RequestParam(required = false) String operator) { + log.info("修改模型打印开关状态changeStatus===>>>参数:{}", JSONUtil.toJsonStr(modelId)); + BpmnTaskDelegateAssigner assignee = null; + if (StringUtils.hasText(operator)) { + assignee = JSON.parseObject(operator, BpmnTaskDelegateAssigner.class); + } + bpmnProcessModelService.changePrintStatus(modelId, status, assignee); + return success(true); + } + /** * 查询流程模型使用的分类列表 * @@ -291,4 +409,349 @@ public class BpmnProcessModelController implements ProcessModelApi { log.info("查询模型的租户集合getTenantIds"); return success(bpmnProcessModelService.getTenantIds()); } + + /** + * 打印模板配置内容 + * + * @param dto + * @return + */ + @Operation(summary = "打印模板配置内容") + @PostMapping("/print/template/upsert") + @Override + public CommonResponse printTemplateConfig(@Validated @RequestBody PrintTemplateConfigUpsertDTO dto) { + log.info("操作打印模板配置内容: {}", JSON.toJSONString(dto)); + bpmnProcessModelService.printTemplateConfig(dto); + return success(); + } + + /** + * 获取打印模板配置内容 + * + * @param dto + * @return + */ + @Operation(summary = "获取打印模板配置内容") + @PostMapping("/print/template/config/query") + @Override + public CommonResponse getPrintTemplateConfig(@Validated @RequestBody PrintTemplateConfigQueryDTO dto) { + log.info("获取打印模板配置内容"); + if (!StringUtils.hasText(dto.getModelId()) && !StringUtils.hasText(dto.getProcessInstanceId()) && !StringUtils.hasText(dto.getProcessDefinitionKey())) { + if (!StringUtils.hasText(dto.getProcessDefinitionKey()) && !StringUtils.hasText(dto.getTenantId())) { + throw new WorkflowEngineException(ILLEGAL_PARAM_ERROR, "processDefinitionKey 与 tenantId 不能同时为空"); + } + throw new WorkflowEngineException(ILLEGAL_PARAM_ERROR, "模型 ID 、实例 ID 和业务 ID 不能同时为空"); + } + if (!StringUtils.hasText(dto.getModelId()) && StringUtils.hasText(dto.getProcessInstanceId())) { + dto.setModelId(bpmnProcessInstanceService.getModelIdByProcessInstanceId(dto.getProcessInstanceId())); + } + if (StringUtils.hasText(dto.getProcessDefinitionKey())) { + BpmnProcessDefinitionVO definition = bpmnProcessDefinitionService.getActiveProcessDefinitionByKey(dto.getProcessDefinitionKey(), dto.getTenantId()); + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + Model model = commandExecutor.execute(new CustomGetModelByDefinitionIdCmd(definition.getId())); + dto.setModelId(model.getId()); + } + return success(bpmnProcessModelService.getPrintTemplateConfig(dto.getModelId())); + } + + /** + * 代运营重置打印模板 + * + * @param dto + * @return + */ + @Operation(summary = "代运营重置打印模板") + @PostMapping(value = "/print/template/config/reset") + public CommonResponse resetPrintTemplateConfig(@Validated @RequestBody RestPrintTemplateConfigDTO dto) { + String activeProcessDefinitionId = bpmnProcessDefinitionService.getActiveProcessDefinitionId(NO_TENANT_ID, dto.getProcessDefinitionKey()); + String modelId = bpmnProcessInstanceService.getModelIdByProcessDefinitionId(null, activeProcessDefinitionId); + PrintModelDTO config = bpmnProcessModelService.getPrintTemplateConfig(modelId); + bpmnProcessModelService.printTemplateConfig(PrintTemplateConfigUpsertDTO.builder() + .modelId(dto.getModelId()) + .printFileName(config.getPrintFileName()) + .printTemplateName(config.getPrintTemplateName()) + .printTemplateConfig(StringUtils.hasText(config.getPrintTemplateConfig()) ? config.getPrintTemplateConfig() : "") + .build()); + return success(true); + } + + /** + * 搜索文档列表 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "搜索文档列表") + @PostMapping("/doc/page") + public CommonResponse> docPage(@Validated @RequestBody DocSearchDTO dto) { + return success(modelDocService.docPage(dto)); + } + + /** + * 获取指定 docIds 文档列表 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "获取指定 docIds 文档列表") + @PostMapping("/doc/ids") + public CommonResponse> docByIds(@Validated @RequestBody DocByIdDTO dto) { + return success(modelDocService.getIds(dto.getIds())); + } + + /** + * 根据业务 ID 获取模型文档列表,自动适配公共模板和代运营 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "根据业务 ID 获取模型文档列表,自动适配公共模板和代运营") + @PostMapping(value = "/doc/list") + public CommonResponse> docList(@Validated @RequestBody DocQueryDTO dto) { + if (!StringUtils.hasText(dto.getProcessInstanceId()) && !StringUtils.hasText(dto.getProcessDefinitionKey())) { + throw new WorkflowEngineException(MODEL_FILE_QUERY_ERROR); + } + return success(modelDocService.docList(dto)); + } + + /** + * 获取关联 HiPrint 类型文档模板内容 + * + * @param fileRelationId + * @return + */ + @Override + @Operation(summary = "获取关联 HiPrint 类型文档模板内容") + @PostMapping(value = "/hi-print/content/get") + public CommonResponse getHiPrintContent(@RequestParam String fileRelationId) { + if (!NumberUtil.isNumber(fileRelationId)) { + throw new WorkflowEngineException(MODEL_FILE_HIPRINT_ID_INVAILD); + } + return success(docContentService.getContent(Long.valueOf(fileRelationId))); + } + + /** + * 添加关联文档 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "添加关联文档") + @PutMapping(value = "/doc/create") + public CommonResponse createDoc(@Validated @RequestBody DocCreateDTO dto) { + return success(modelDocService.createDoc(dto, true) > 0); + } + + /** + * 修改关联文档 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "修改关联文档") + @PostMapping(value = "/doc/update") + public CommonResponse updateDoc(@Validated @RequestBody DocUpdateDTO dto) { + return success(modelDocService.updateDoc(dto)); + } + + /** + * 克隆关联文档 + * + * @param docId + * @return + */ + @Override + @Operation(summary = "克隆关联文档") + @PostMapping(value = "/doc/clone") + public CommonResponse cloneDoc(@RequestParam("id") Long docId) { + return success(this.singleCloneDoc(docId, null)); + } + + /** + * 单个复制文档模版 + * + * @param docId 复制的模版id + * @param docModelInfo 模版关联的审批业务模版信息 + * @return 是否成功 + */ + private Boolean singleCloneDoc(Long docId, DocModelInfo docModelInfo) { + DocBaseVO originDoc = modelDocService.get(docId); + ArrayList wpsSupported = Lists.newArrayList(WORD, EXCEL); + if (wpsSupported.contains(originDoc.getFileType())) { + // WPS 文档 clone + CopyNodeRequest copy = new CopyNodeRequest(); + copy.setCode(originDoc.getFileRelationId()); + CommonResponse response = docAnonymousApi.copy(copy); + if (Objects.isNull(response) || !Objects.equals(response.getCode(), 200)) { + throw new WorkflowEngineException(MODEL_FILE_CLONE_ERROR, Objects.isNull(response) ? "网络异常" : response.getMsg()); + } + DocCreateDTO newDoc = BeanMapper.copyBean(originDoc, DocCreateDTO.class); + newDoc.setFileRelationId(response.getData()); + newDoc.setTag(""); + if (docModelInfo != null) { + newDoc.setModelId(docModelInfo.getTargetModelId()); + newDoc.setModelKey(docModelInfo.getTargetModelKey()); + newDoc.setTenantId(docModelInfo.getTenantId()); + newDoc.setTag(docModelInfo.getTag()); + } + return modelDocService.createDoc(newDoc, false) > 0; + } + if (Objects.equals(originDoc.getFileType(), FileTypeEnum.PDF)) { + // PDF 文档 clone + CommonResponse response = fileServiceApi.batchCopyObject(ServerFileBatchCopyObjectRequest.builder() + .copyObjects(Sets.newHashSet(ServerFileBatchCopyObjectRequest.ServerFileCopyObjectRequest.builder() + .srcFileKey(originDoc.getFileRelationId()) + .build())) + .build()); + if (Objects.isNull(response) || Objects.equals(response.getCode(), 200)) { + throw new WorkflowEngineException(MODEL_FILE_CLONE_ERROR, Objects.isNull(response) ? "网络异常" : response.getMsg()); + } + DocCreateDTO newDoc = BeanMapper.copyBean(originDoc, DocCreateDTO.class); + newDoc.setFileRelationId(response.getData().getResponses().get(0).getSrcFileKey()); + newDoc.setTag(""); + if (docModelInfo != null) { + newDoc.setModelId(docModelInfo.getTargetModelId()); + newDoc.setModelKey(docModelInfo.getTargetModelKey()); + newDoc.setTenantId(docModelInfo.getTenantId()); + newDoc.setTag(docModelInfo.getTag()); + } + return modelDocService.createDoc(newDoc, false) > 0; + } + return modelDocService.cloneDoc(DocCloneDTO.builder() + .docId(docId) + .targetTenantId(docModelInfo.getTenantId()) + .targetModelId(docModelInfo.getTargetModelId()) + .targetFileTag(docModelInfo.getTag()) + .build()) > 0; + } + + /** + * 删除指定文档 + * + * @param docId + * @return + */ + @Override + @Operation(summary = "删除指定文档") + @DeleteMapping(value = "/doc/delete") + public CommonResponse deleteDoc(@RequestParam("id") Long docId) { + Boolean result = modelDocService.deleteDoc(docId); + if (Objects.equals(Boolean.TRUE, result)) { + DocBaseVO doc = modelDocService.get(docId, true); + if (Objects.equals(WORD, doc.getFileType()) || Objects.equals(EXCEL, doc.getFileType())) { + wpsUtil.deleteWpsFile(doc.getFileRelationId()); + } + } + return success(result); + } + + /** + * 关联文档配置排序 + * + * @param dto + * @return + */ + @Operation(summary = "关联文档配置排序") + @PostMapping(value = "/doc/order") + @Override + public CommonResponse orderDoc(@Validated @RequestBody DocOrderDTO dto) { + return success(modelDocService.orderDoc(dto)); + } + + /** + * 设置关联文档状态 + * + * @param dto + * @return + */ + @Operation(summary = "设置关联文档状态") + @PostMapping(value = "/doc/status") + public CommonResponse statusDoc(@Validated @RequestBody DocStatusDTO dto) { + return success(modelDocService.statusDoc(dto)); + } + + /** + * 设置关联文档的必选状态 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "设置关联文档的必选状态") + @PostMapping(value = "/doc/require") + public CommonResponse requireDoc(@Validated @RequestBody DocStatusDTO dto) { + return success(modelDocService.requireDoc(dto)); + } + + /** + * 特殊的查询设置过关联过文档的工作台 ID 集合 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "特殊的查询设置过关联过文档的工作台 ID 集合") + @PostMapping(value = "/has/docs/tenantId") + public CommonResponse> hasFilesTenantIds(@Validated @RequestBody DocTenantQueryDTO dto) { + return success(modelDocService.querySetting(dto.getProcessDefinitionKey(), null) + .stream().map(ExtAxModelDoc::getTenantId) + .filter(StringUtils::hasText) + .map(Long::parseLong) + .distinct() + .collect(Collectors.toList())); + } + + /** + * 重置模版关联文档 + * + * @param dto + * @return + */ + @Override + @Operation(summary = "重置模版关联文档") + @PostMapping(value = "/doc/reset") + public CommonResponse resetDoc(@Validated @RequestBody DocResetDTO dto) { + DocSearchDTO docSearchDTO = new DocSearchDTO(); + docSearchDTO.setModelId(dto.getModelId()); + docSearchDTO.setTenantId(dto.getWorkspaceId()); + docSearchDTO.setPageNo(1); + docSearchDTO.setPageSize(999); + BpmPageResult docBaseVOBpmPageResult = modelDocService.docPage(docSearchDTO); + List docBaseVOS = ListUtils.emptyIfNull(docBaseVOBpmPageResult.getList()); + //先删除 + if (!CollectionUtils.isEmpty(docBaseVOS)) { + modelDocService.batchDeleteDoc(docBaseVOS.stream().map(DocBaseVO::getId).collect(Collectors.toList())); + } + + CommandExecutor commandExecutor = springProcessEngineConfiguration.getCommandExecutor(); + List commonDocBaseVos = commandExecutor.execute(new CustomGetModelDocsCmd(null, + dto.getCategory(), null, false, false, extAxModelDocMapper, reModelService)); + //通用模板的文档 + if (!CollectionUtils.isEmpty(commonDocBaseVos)) { + //进行克隆 + commonDocBaseVos.forEach(cb -> this.singleCloneDoc(cb.getId(), DocModelInfo.builder() + .targetModelKey(dto.getCategory()) + .targetModelId(dto.getModelId()) + .tenantId(dto.getWorkspaceId()) + .tag(cb.getTag()) + .build())); + } + return CommonResponse.success(Boolean.TRUE); + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class DocModelInfo { + private String targetModelId; + private String targetModelKey; + private String tenantId; + private String tag; + } } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java index 7589a5a9b..cbc527af5 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java @@ -2,27 +2,10 @@ package cn.axzo.workflow.server.controller.web.bpmn; import cn.axzo.workflow.client.feign.bpmn.ProcessTaskApi; import cn.axzo.workflow.common.enums.AttachmentTypeEnum; -import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO; -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.BpmnTaskAuditWithFormDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO; -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.BpmnTaskDelegateAssigner; -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.request.bpmn.task.*; 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.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.axzo.workflow.common.model.response.bpmn.task.*; import cn.axzo.workflow.core.service.BpmnProcessTaskService; import cn.axzo.workflow.server.common.annotation.ErrorReporter; import cn.axzo.workflow.server.common.annotation.RepeatSubmit; @@ -33,14 +16,10 @@ import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.ListUtils; import org.flowable.form.api.FormInfo; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Nullable; import javax.annotation.Resource; @@ -128,7 +107,11 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp dto.setAttachmentList(tempAttachments); } populateUsersAvatar(dto.getApprover()); - bpmnProcessTaskService.approveTaskWithForm(dto); + if (CollectionUtils.isEmpty(dto.getFormVariables())) { + bpmnProcessTaskService.approveTask(dto); + } else { + bpmnProcessTaskService.approveTaskWithForm(dto); + } return success(true); } @@ -148,6 +131,7 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp /** * 获取当前节点可回退节点选项列表 + * * @param taskId 当前任务id * @return 可选回退节点列表 */ @@ -155,7 +139,7 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp @GetMapping("/back/optional/nodes") @Override public CommonResponse> getBackOptionalNodes(@RequestParam @NotBlank(message = "任务id不能为空") String taskId) { - List approveOptionalNodes = bpmnProcessTaskService.getBackOptionalNodes(taskId); + List approveOptionalNodes = bpmnProcessTaskService.getBackOptionalNodesByTaskId(taskId); return success(approveOptionalNodes); } @@ -173,6 +157,19 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp return success(true); } + /** + * 回退到指定节点 + */ + @Operation(summary = "系统操作回退任务到指定节点") + @PostMapping("/system/back") + @RepeatSubmit + @Override + public CommonResponse systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto) { + log.info("系统回退 systemBackTask===>>>参数:{}", JSON.toJSONString(dto)); + bpmnProcessTaskService.systemBackTask(dto); + return success(true); + } + /** * 驳回 */ @@ -404,4 +401,5 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp @RequestParam @NotBlank(message = "自然人 ID 不能为空") String personId) { return success(bpmnProcessTaskService.findTaskIdByInstanceIdsAndPersonId(processInstanceIds, personId)); } + } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/PrintAdminController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/PrintAdminController.java new file mode 100644 index 000000000..9ff3009be --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/PrintAdminController.java @@ -0,0 +1,411 @@ +package cn.axzo.workflow.server.controller.web.manage; + +import cn.axzo.maokai.api.client.OrganizationalNodeUserQueryApi; +import cn.axzo.maokai.api.vo.request.OrgNodeUserBriefInfoListReq; +import cn.axzo.maokai.api.vo.response.OrgNodeUserBriefInfoResp; +import cn.axzo.workflow.client.feign.manage.PrintAdminApi; +import cn.axzo.workflow.common.enums.VarTypeEnum; +import cn.axzo.workflow.common.exception.WorkflowEngineException; +import cn.axzo.workflow.common.model.dto.print.FieldAttributeDTO; +import cn.axzo.workflow.common.model.dto.print.PrintFieldDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintFieldQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessInstanceLogQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.common.model.request.category.CategoryGroupVarSearchDto; +import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; +import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO; +import cn.axzo.workflow.common.model.response.category.CategoryGroupVarItemVo; +import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper; +import cn.axzo.workflow.core.engine.cmd.CustomGetFormInstanceLatestValuesCmd; +import cn.axzo.workflow.core.engine.cmd.CustomGetProcessInstanceVariablesCmd; +import cn.axzo.workflow.core.service.BpmnProcessDefinitionService; +import cn.axzo.workflow.core.service.BpmnProcessInstanceService; +import cn.axzo.workflow.core.service.CategoryGroupService; +import cn.axzo.workflow.server.common.annotation.ErrorReporter; +import cn.axzo.workflow.server.common.util.RpcExternalUtil; +import cn.axzo.workflow.server.controller.web.bpmn.BpmnProcessInstanceController; +import cn.azxo.framework.common.model.CommonResponse; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.form.api.FormInfo; +import org.flowable.form.api.FormRepositoryService; +import org.flowable.form.model.FormContainer; +import org.flowable.form.model.FormField; +import org.flowable.form.model.FormFieldTypes; +import org.flowable.form.model.SimpleFormModel; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.validation.constraints.NotBlank; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static cn.axzo.workflow.common.code.FormModelRespCode.FORM_MODEL_NOT_EXISTS; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_IMAGE; +import static cn.axzo.workflow.common.constant.FormConstants.FORM_FIELD_TYPE_INPUT; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_DEFINITION_KEY; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_DEFINITION_KEY_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_END_TIME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_END_TIME_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_NAME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_NAME_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_PHONE; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INSTANCE_ID; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_INSTANCE_ID_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOGS; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOGS_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_ACTIVITY_NAME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_ACTIVITY_NAME_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_ADVICE; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_ADVICE_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_APPROVER_NAME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_APPROVER_NAME_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_OPERATION_TIME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_OPERATION_TIME_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_POSITION; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_POSITION_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_SIGNATURE; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_SIGNATURE_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_UNIT; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_LOG_UNIT_DESC; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_START_TIME; +import static cn.axzo.workflow.common.constant.VariableConstants.PRINT_VAR_PROCESS_START_TIME_DESC; +import static cn.axzo.workflow.common.enums.PrintFieldCategoryEnum.form; +import static cn.axzo.workflow.common.enums.PrintFieldCategoryEnum.sign; +import static cn.axzo.workflow.common.enums.PrintFieldCategoryEnum.signature; +import static cn.axzo.workflow.common.enums.PrintFieldCategoryEnum.system; +import static cn.azxo.framework.common.model.CommonResponse.success; + +/** + * 打印相关的控制器 + * + * @author wangli + * @since 2025-01-16 17:48 + */ +@Slf4j +@RequestMapping({"/web/v1/api/print/admin", "/api/print/admin"}) +@RestController +@ErrorReporter +@Validated +public class PrintAdminController implements PrintAdminApi { + + @Resource + private FormRepositoryService formRepositoryService; + @Resource + private BpmnProcessDefinitionService processDefinitionService; + @Resource + private SpringProcessEngineConfiguration processEngineConfiguration; + @Resource + private OrganizationalNodeUserQueryApi organizationalNodeUserQueryApi; + @Resource + private BpmnProcessInstanceController bpmnProcessInstanceController; + @Resource + private BpmnProcessInstanceService bpmnProcessInstanceService; + @Resource + private CategoryGroupService categoryGroupService; + + /** + * 查询指定流程实例是否能使用打印 + * + * @param processInstanceId + * @return + */ + @Operation(summary = "查询指定流程实例是否能使用打印") + @Override + @GetMapping("/template/exists") + public CommonResponse hasPrintTemplate(@NotBlank(message = "流程实例不能为空") @RequestParam String processInstanceId) { + return CommonResponse.success(bpmnProcessInstanceService.hasPrintTemplate(processInstanceId, null)); + } + + /** + * 获取打印模板中可打印的字段, 或者是 WPS 模板中可配置的变量字段 + * + * @param dto + * @return + */ + @Operation(summary = "获取打印模板中可打印的字段, 或者是 WPS 模板中可配置的变量字段") + @PostMapping("/fields") + @Override + public CommonResponse> getPrintFields(@Validated @RequestBody PrintFieldQueryDTO dto) { + List printFields = new ArrayList<>(); + FormInfo formModel; + try { + formModel = formRepositoryService.getFormModelByKey(dto.getProcessDefinitionKey(), dto.getTenantId(), false); + // 表单字段 + List formFields = ((SimpleFormModel) formModel.getFormModel()).getFields(); + formFields.forEach(formField -> { + FormContainer formContainer = (FormContainer) formField; + printFields.addAll(formContainer.getFields().get(0).stream().map(field -> { + PrintFieldDTO printFieldDTO = new PrintFieldDTO() + .setName(field.getName()) + .setCode(field.getId()) + .setFieldCategoryType(form) + .setFieldFormType(field.getType()); + switchAmount(field, printFieldDTO); + switchFormContainer(field, printFieldDTO); + return printFieldDTO; + } + ).collect(Collectors.toList())); + }); + } catch (FlowableObjectNotFoundException e) { + log.warn("can't found form model"); + if (Objects.equals(Boolean.TRUE, dto.getThrowException())) { + throw new WorkflowEngineException(FORM_MODEL_NOT_EXISTS); + } + } + + // 生成固定的系统字段 + printFields.addAll(generateSystemFields(dto.getProcessDefinitionKey(), dto.getTenantId())); + + // 生成业务对应的变量相应字段 + // generateDictVariables(printFields, dto.getProcessDefinitionKey()); + + return success(printFields); + } + + // 自定义组件类型的额外处理 + private static void switchFormContainer(FormField field, PrintFieldDTO printFieldDTO) { + if (field instanceof FormContainer) { + FormContainer container = (FormContainer) field; + printFieldDTO.setAttributes(container.getFields().get(0) + .stream().map(subField -> new FieldAttributeDTO() + .setCode(subField.getId()) + .setFieldFormType(subField.getType()) + .setName(subField.getName())) + .collect(Collectors.toList())); + } + } + + // 金额组件类型的额外处理 + private static void switchAmount(FormField field, PrintFieldDTO printFieldDTO) { + if (Objects.equals(field.getType(), FormFieldTypes.AMOUNT)) { + List attributes = new ArrayList<>(); + attributes.add(new FieldAttributeDTO() + .setCode("standardNumerals") + .setName("小写") + .setFieldFormType(FORM_FIELD_TYPE_INPUT) + ); + if (Boolean.parseBoolean(field.getParam("toUpper").toString())) { + attributes.add(new FieldAttributeDTO() + .setCode("uppercaseNumerals") + .setName("大写") + .setFieldFormType(FORM_FIELD_TYPE_INPUT)); + } + printFieldDTO.setAttributes(attributes); + } + } + + private List generateDictVariables(List printFields, String processDefinitionKey) { + if (CollectionUtils.isEmpty(printFields)) { + printFields = new ArrayList<>(); + } + // 加载业务管理中自定义的业务变量 + List categoryGroupVarItemVos = categoryGroupService.searchGroupAndVarList(CategoryGroupVarSearchDto.builder() + .category(processDefinitionKey) + .build()); + if (CollectionUtils.isEmpty(categoryGroupVarItemVos)) { + return printFields; + } + + printFields.addAll(categoryGroupVarItemVos.stream().map(i -> + new PrintFieldDTO().setName(i.getGroupName()).setCode("group_" + i.getId()).setFieldCategoryType(sign) + .setAttributes(i.getVars().stream().map(j -> new FieldAttributeDTO() + .setName(j.getName()) + .setCode(j.getCode()) + .setFieldFormType(mappingFormFieldType(j.getType()))) + .collect(Collectors.toList()) + )).collect(Collectors.toList())); + return printFields; + } + + private String mappingFormFieldType(VarTypeEnum varTypeEnum) { + switch (varTypeEnum) { + case TEXT: + return FORM_FIELD_TYPE_INPUT; + case PICTURE: + return FORM_FIELD_TYPE_IMAGE; + default: + return ""; + } + } + + private List generateSystemFields(String processDefinitionKey, String tenantId) { + List printFields = new ArrayList<>(); + + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_DEFINITION_KEY_DESC).setCode(PRINT_VAR_PROCESS_DEFINITION_KEY).setFieldCategoryType(system).setFieldFormType("input")); + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_INSTANCE_ID_DESC).setCode(PRINT_VAR_PROCESS_INSTANCE_ID).setFieldCategoryType(system).setFieldFormType("input")); + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_START_TIME_DESC).setCode(PRINT_VAR_PROCESS_START_TIME).setFieldCategoryType(system).setFieldFormType("input")); + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_END_TIME_DESC).setCode(PRINT_VAR_PROCESS_END_TIME).setFieldCategoryType(system).setFieldFormType("input")); + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_INITIATOR_NAME_DESC).setCode(PRINT_VAR_PROCESS_INITIATOR_NAME).setFieldCategoryType(system).setFieldFormType("input")); + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_INITIATOR_POSITION_DESC).setCode(PRINT_VAR_PROCESS_INITIATOR_POSITION).setFieldCategoryType(system).setFieldFormType("input")); + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_INITIATOR_PHONE_DESC).setCode(PRINT_VAR_PROCESS_INITIATOR_PHONE).setFieldCategoryType(system).setFieldFormType("input")); + printFields.add(new PrintFieldDTO().setName(PRINT_VAR_PROCESS_LOGS_DESC).setCode(PRINT_VAR_PROCESS_LOGS).setFieldCategoryType(system).setFieldFormType("table") + .setAttributes(Lists.newArrayList( + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_ACTIVITY_NAME).setName(PRINT_VAR_PROCESS_LOG_ACTIVITY_NAME_DESC), + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_APPROVER_NAME).setName(PRINT_VAR_PROCESS_LOG_APPROVER_NAME_DESC), + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_UNIT).setName(PRINT_VAR_PROCESS_LOG_UNIT_DESC), + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_POSITION).setName(PRINT_VAR_PROCESS_LOG_POSITION_DESC), + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_ADVICE).setName(PRINT_VAR_PROCESS_LOG_ADVICE_DESC), + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_OPERATION_TIME).setName(PRINT_VAR_PROCESS_LOG_OPERATION_TIME_DESC), + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_SIGNATURE).setName(PRINT_VAR_PROCESS_LOG_SIGNATURE_DESC) + ))); + + // 电子签名 + BpmnProcessDefinitionVO definition = processDefinitionService.getActiveProcessDefinitionByKey(processDefinitionKey, tenantId); + printFields.addAll(processDefinitionService.findFlowElements(definition.getId()) + .stream() + .filter(BpmnMetaParserHelper::getActivitySignature) + .map(activity -> new PrintFieldDTO() + .setName(activity.getName()) + .setCode(activity.getId()) + .setFieldCategoryType(signature) + .setFieldFormType("signature") + .setAttributes(Lists.newArrayList(new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_SIGNATURE).setName(PRINT_VAR_PROCESS_LOG_SIGNATURE_DESC).setFieldFormType(FORM_FIELD_TYPE_INPUT), + new FieldAttributeDTO().setCode(PRINT_VAR_PROCESS_LOG_ADVICE).setName(PRINT_VAR_PROCESS_LOG_ADVICE_DESC).setFieldFormType(FORM_FIELD_TYPE_INPUT)))) + .collect(Collectors.toList()) + ); + + return printFields; + } + + /** + * 获取指定流程下用于替换打印的相关变量 + * + * @param processInstanceId + * @return + */ + @Operation(summary = "获取指定流程下用于替换打印的相关变量") + @GetMapping("/field/variables") + @Override + public CommonResponse> getPrintFieldVariables(@NotBlank(message = "流程实例不能为空") @RequestParam String processInstanceId) { + Map result = new HashMap<>(); + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + byte[] formInstanceValue = commandExecutor.execute(new CustomGetFormInstanceLatestValuesCmd(processInstanceId)); + if (!ObjectUtils.isEmpty(formInstanceValue)) { + JSONObject treeNode = JSON.parseObject(new String(formInstanceValue)); + result.putAll(treeNode.getJSONObject("values").getInnerMap()); + } + + // 生成系统字段的变量 + generateSystemFieldVariables(processInstanceId, result); + + // 将所有变量都转换成 JSON 对象返回 + convertValueToObject(result); + return success(result); + } + + private void convertValueToObject(Map result) { + if (CollectionUtils.isEmpty(result)) { + return; + } + + result.forEach((key, value) -> { + if (!(value instanceof String)) { + return; + } + if (((String) value).startsWith("[")) { + result.put(key, JSONArray.parseArray((String) value)); + } else if (((String) value).startsWith("{")) { + result.put(key, JSONObject.parseObject((String) value)); + } + }); + } + + private void generateSystemFieldVariables(String processInstanceId, Map result) { + CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor(); + Map variables = commandExecutor.execute(new CustomGetProcessInstanceVariablesCmd(processInstanceId)); + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); + // 解析发起人 + BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(variables.getOrDefault(PRINT_VAR_PROCESS_INITIATOR, null)); + if (Objects.nonNull(initiator)) { + Optional user = getUserInfo(initiator); + variables.put(PRINT_VAR_PROCESS_INITIATOR_NAME, user.isPresent() ? user.get().getRealName() : ""); + variables.put(PRINT_VAR_PROCESS_INITIATOR_POSITION, user.isPresent() && Objects.nonNull(user.get().getJob()) ? user.get().getJob().getName() : ""); + variables.put(PRINT_VAR_PROCESS_INITIATOR_PHONE, user.isPresent() ? user.get().getProfile().getPhone() : ""); + variables.remove(PRINT_VAR_PROCESS_INITIATOR); + } + // 填充审批日志 + CommonResponse processInstanceLogs = bpmnProcessInstanceController + .getProcessInstanceLogs(BpmnProcessInstanceLogQueryDTO.builder() + .processInstanceId(processInstanceId) + .hasButton(false) + .encrypt(false) + .build()); + List> taskLogs = new ArrayList<>(); + processInstanceLogs.getData().getTaskDetails().stream() + .filter(e -> !e.isVirtual() && Objects.nonNull(e.getEndTime())) + .forEach(taskLog -> { + Map taskLogMap = new HashMap<>(); + taskLogMap.put(PRINT_VAR_PROCESS_LOG_ACTIVITY_NAME, taskLog.getName()); + Optional user = getUserInfo(taskLog.getAssigneeSnapshot()); + taskLogMap.put(PRINT_VAR_PROCESS_LOG_APPROVER_NAME, user.isPresent() ? user.get().getRealName() : ""); + taskLogMap.put(PRINT_VAR_PROCESS_LOG_UNIT, user.isPresent() ? user.get().getOrganizationalUnitName() : ""); + taskLogMap.put(PRINT_VAR_PROCESS_LOG_POSITION, user.isPresent() && Objects.nonNull(user.get().getJob()) ? user.get().getJob().getName() : ""); + taskLogMap.put(PRINT_VAR_PROCESS_LOG_ADVICE, taskLog.getAdvice()); + taskLogMap.put(PRINT_VAR_PROCESS_LOG_OPERATION_TIME, sdf.format(taskLog.getCreateTime())); + taskLogMap.put(PRINT_VAR_PROCESS_LOG_SIGNATURE, taskLog.getSignatureUrl()); + taskLogs.add(taskLogMap); + variables.put(PRINT_VAR_PROCESS_LOGS, taskLogs); + }); + result.putAll(variables); + } + + private Optional getUserInfo(BpmnTaskDelegateAssigner assigner) { + if (Objects.isNull(assigner)) { + return Optional.empty(); + } + OrgNodeUserBriefInfoListReq req = new OrgNodeUserBriefInfoListReq(); + if (StringUtils.hasText(assigner.getTenantId())) { + req.setWorkspaceId(Long.valueOf(assigner.getTenantId())); + } + if (StringUtils.hasText(assigner.getOuId())) { + req.setOuId(Long.valueOf(assigner.getOuId())); + } + if (StringUtils.hasText(assigner.getNodeId())) { + req.setOrgNodeIds(Lists.newArrayList(Long.valueOf(assigner.getNodeId()))); + } + if (StringUtils.hasText(assigner.getPersonId())) { + req.setPersonIds(Lists.newArrayList(Long.valueOf(assigner.getPersonId()))); + } + req.setNeedJob(true); + req.setNeedUnit(true); + req.setNeedProfile(true); + req.setContainsExited(true); + List users = RpcExternalUtil.rpcApiResultProcessor(() -> organizationalNodeUserQueryApi.listOrgNodeUsers(req), + "查询审批人员组织信息", req); + + if (CollectionUtils.isEmpty(users)) { + return Optional.empty(); + } + + return users.stream().sorted(Comparator.comparing(OrgNodeUserBriefInfoResp::getExited).reversed()) + .collect(Collectors.toList()).stream().findFirst(); + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessCategoryController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessCategoryController.java index a14f1e4aa..6922a2542 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessCategoryController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessCategoryController.java @@ -1,36 +1,26 @@ package cn.axzo.workflow.server.controller.web.manage; import cn.axzo.workflow.client.feign.manage.ProcessCategoryApi; -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; -import cn.axzo.workflow.common.model.request.category.CategorySearchDTO; -import cn.axzo.workflow.common.model.request.category.CategoryUpdateDTO; +import cn.axzo.workflow.common.model.request.category.*; 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.CategoryGroupVarItemVo; import cn.axzo.workflow.common.model.response.category.CategoryItemVO; import cn.axzo.workflow.core.service.CategoryConfigService; import cn.axzo.workflow.core.service.CategoryService; +import cn.axzo.workflow.core.service.CategoryGroupService; import cn.axzo.workflow.server.common.annotation.ErrorReporter; import cn.axzo.workflow.server.common.annotation.RepeatSubmit; import cn.azxo.framework.common.model.CommonResponse; import io.swagger.v3.oas.annotations.Operation; import lombok.extern.slf4j.Slf4j; 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.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; -import static cn.azxo.framework.common.model.CommonResponse.success; +import static cn.azxo.framework.common.model.CommonResponse.*; /** * 业务分类相关控制器 @@ -49,6 +39,8 @@ public class ProcessCategoryController implements ProcessCategoryApi { private CategoryService categoryService; @Resource private CategoryConfigService categoryConfigService; + @Resource + private CategoryGroupService categoryGroupService; /** * 获取指定业务分类 @@ -228,4 +220,28 @@ public class ProcessCategoryController implements ProcessCategoryApi { public CommonResponse checkCategoryStatus(@RequestParam Long tenantId, @RequestParam String categoryCode) { return success(categoryService.checkCategoryStatus(tenantId, categoryCode)); } + + /** + * 业务分类的分组和变量查询 + * + * @return 业务分类的分组以及变量 + */ + @Operation(summary = "业务分类的分组和变量查询") + @PostMapping("/group-with-vars/list") + @Override + public CommonResponse> searchCategoryGroupAndVars(@Validated @RequestBody CategoryGroupVarSearchDto dto) { + return success(categoryGroupService.searchGroupAndVarList(dto)); + } + + /** + * 新增/更新 业务分类的分组和变量 + * + * @return 是否成功 + */ + @Operation(summary = "新增/更新 业务分类的分组和变量") + @PostMapping("/group-with-vars/upsert") + @Override + public CommonResponse upsertCategoryGroupAndVars(@Validated @RequestBody CategoryGroupVarUpsertDto dto) { + return success(categoryGroupService.upsertGroupAndVars(dto)); + } } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessConfigController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessConfigController.java index 2201cd9bf..4d7a12b62 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessConfigController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/manage/ProcessConfigController.java @@ -46,6 +46,9 @@ public class ProcessConfigController implements ProcessConfigApi { .setBtnKey(buttonEnum.getBtnKey()) .setBtnName(buttonEnum.getBtnName()) .setChecked(false) - .setDisabled(false)).collect(Collectors.toList())); + .setDisabled(false) + .setSupportBizType(buttonEnum.getSupportBizType()) + ) + .collect(Collectors.toList())); } } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/SelfBoradcastRocketConfiguration.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/SelfBoradcastRocketConfiguration.java index 77969ded1..f8f0bac78 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/SelfBoradcastRocketConfiguration.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/SelfBoradcastRocketConfiguration.java @@ -3,12 +3,7 @@ package cn.axzo.workflow.server.mq.inside; import cn.axzo.framework.rocketmq.BaseListener; import cn.axzo.framework.rocketmq.EventConsumer; import org.apache.rocketmq.common.message.MessageExt; -import org.apache.rocketmq.spring.annotation.ConsumeMode; -import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; import org.apache.rocketmq.spring.core.RocketMQListener; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; import javax.annotation.Resource; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchBatchSyncListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchBatchSyncListener.java index 5c2e21069..2f9e38f0e 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchBatchSyncListener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchBatchSyncListener.java @@ -63,7 +63,7 @@ public class ElasticSearchBatchSyncListener { // consumer.setConsumeThreadMax(1); // consumer.setConsumeThreadMin(1); consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { - log.warn("batch get msg size: {}", msgs.size()); + log.info("batch get msg size: {}", msgs.size()); if(CollectionUtils.isEmpty(msgs)) { return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } @@ -77,7 +77,7 @@ public class ElasticSearchBatchSyncListener { idSet.add(processInstanceId); } } - log.warn("deduplication ids size: {}", idSet.size()); + log.info("deduplication ids size: {}", idSet.size()); idSet.forEach(this::sync); } catch (Exception e) { return ConsumeConcurrentlyStatus.RECONSUME_LATER; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchSyncListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchSyncListener.java index deca64a9d..4290b7015 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchSyncListener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/mq/inside/consumer/ElasticSearchSyncListener.java @@ -10,12 +10,9 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; -import org.flowable.engine.impl.util.CommandContextUtil; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; -import javax.annotation.Resource; import java.util.List; /** diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/CompletedProcessInstanceSyncEsJobHandler.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/CompletedProcessInstanceSyncEsJobHandler.java index cf09d1f2d..7c12649c1 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/CompletedProcessInstanceSyncEsJobHandler.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/CompletedProcessInstanceSyncEsJobHandler.java @@ -18,10 +18,8 @@ import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.collect.Lists; import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; -import com.xxl.job.core.log.XxlJobLogger; -import com.xxl.job.core.util.ShardingUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.common.engine.impl.interceptor.CommandExecutor; @@ -58,7 +56,7 @@ import static cn.axzo.workflow.server.controller.delegate.AbstractBpmnTaskAssign @Component @RequiredArgsConstructor @Slf4j -public class CompletedProcessInstanceSyncEsJobHandler extends IJobHandler { +public class CompletedProcessInstanceSyncEsJobHandler { private final BpmnProcessInstanceForEsService bpmnProcessInstanceForEsService; private final AggregateProcessInstanceService aggregateProcessInstanceService; private final SpringProcessEngineConfiguration springProcessEngineConfiguration; @@ -69,14 +67,13 @@ public class CompletedProcessInstanceSyncEsJobHandler extends IJobHandler { @XxlJob("completedProcessInstanceSyncToEs") public ReturnT execute(String s) { // 分片参数 - ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo(); - if (Objects.nonNull(shardingVO) && shardingVO.getTotal() > 1) { - log.info("分片参数:当前分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal()); - XxlJobLogger.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal()); - } + int shardIndex = XxlJobHelper.getShardIndex(); + int shardTotal = XxlJobHelper.getShardTotal(); + log.info("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); + XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); log.debug("start exec finished process instance data sync... "); - XxlJobLogger.log("start exec finished process instance data sync... "); + XxlJobHelper.log("start exec finished process instance data sync... "); HistoricProcessInstanceSearchForEsDTO search = new HistoricProcessInstanceSearchForEsDTO(); search.setFinished(true); @@ -87,25 +84,25 @@ public class CompletedProcessInstanceSyncEsJobHandler extends IJobHandler { if (StringUtils.hasText(s)) { search = JSON.parseObject(s, HistoricProcessInstanceSearchForEsDTO.class); log.info("根据入参转换后的查询入参:{}", JSON.toJSONString(search)); - XxlJobLogger.log("根据入参转换后的查询入参:{}", JSON.toJSONString(search)); + XxlJobHelper.log("根据入参转换后的查询入参:{}", JSON.toJSONString(search)); } else { log.info("入参为空, 将以默认条件执行"); - XxlJobLogger.log("入参为空, 将以默认条件执行"); + XxlJobHelper.log("入参为空, 将以默认条件执行"); } } catch (Exception e) { log.warn("无法解析传参, 入参必须是 JSON 格式, 入参可参考 HistoricProcessInstanceSearchForEsDTO 模型"); - XxlJobLogger.log("无法解析传参, 入参必须是 JSON 格式, 入参可参考 HistoricProcessInstanceSearchForEsDTO 模型"); + XxlJobHelper.log("无法解析传参, 入参必须是 JSON 格式, 入参可参考 HistoricProcessInstanceSearchForEsDTO 模型"); return ReturnT.FAIL; } // 开始同步完成的实例到 ES - DataSyncSummaryDTO summary = doSync(search, shardingVO.getTotal() > 1 ? shardingVO : null); + DataSyncSummaryDTO summary = doSync(search, shardIndex, shardTotal); // 执行完同步后的一些额外操作 afterSync(endTime); log.info("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); - XxlJobLogger.log("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); + XxlJobHelper.log("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); return ReturnT.SUCCESS; } @@ -124,30 +121,29 @@ public class CompletedProcessInstanceSyncEsJobHandler extends IJobHandler { * 内部自动分页循环同步 * * @param search - * @param shardingVO 分片信息, 可能为 null * @return 应同步至 ES 的统计数据 */ - private DataSyncSummaryDTO doSync(HistoricProcessInstanceSearchForEsDTO search, ShardingUtil.ShardingVO shardingVO) { + private DataSyncSummaryDTO doSync(HistoricProcessInstanceSearchForEsDTO search, int shardIndex, int shardTotal) { Long totalProcessInstanceCount = bpmnProcessInstanceForEsService.queryHistoricProcessInstanceTotalCount(search); log.info("查询到待同步实例维度数据量: {} 条", totalProcessInstanceCount); - XxlJobLogger.log("查询到待同步实例维度数据量: {} 条", totalProcessInstanceCount); + XxlJobHelper.log("查询到待同步实例维度数据量: {} 条", totalProcessInstanceCount); AtomicLong totalProcessTaskCount = new AtomicLong(0); IntStream.iterate(0, i -> i + search.getOverPageSize()).limit((totalProcessInstanceCount + search.getOverPageSize() - 1) / search.getOverPageSize()) .forEach(skipRows -> { log.info("处理进度: {} %, current startRow: {}, endRow: {}", String.format("%.2f", (double) skipRows / totalProcessInstanceCount * 100), skipRows, skipRows + search.getOverPageSize()); - XxlJobLogger.log("处理进度: {} %, current startRow: {}, endRow: {}", String.format("%.2f", (double) skipRows / totalProcessInstanceCount * 100), + XxlJobHelper.log("处理进度: {} %, current startRow: {}, endRow: {}", String.format("%.2f", (double) skipRows / totalProcessInstanceCount * 100), skipRows, skipRows + search.getOverPageSize()); int pageNo = skipRows / search.getOverPageSize() + 1; List instances = bpmnProcessInstanceForEsService.queryHistoricProcessInstance(search, new Page(pageNo, search.getOverPageSize())); instances.parallelStream().filter(hpi -> { - if (Objects.nonNull(shardingVO)) { + if (shardIndex >= 0 && shardTotal >= 0) { if (hpi.getId().contains("-")) { // 最开始的实例编号是 UUID,无法取模,所以统一交给第一个节点处理 - return shardingVO.getIndex() == 0; + return shardIndex == 0; } - return Long.parseLong(hpi.getId().substring(12)) % shardingVO.getTotal() == shardingVO.getIndex(); + return Long.parseLong(hpi.getId().substring(12)) % shardTotal == shardIndex; } // 如果配置的非分片广播的方式,那么 shardingVO 将是 null return true; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/DeadLetterJobRetryHandler.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/DeadLetterJobRetryHandler.java index ebae7878f..72ac7ae57 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/DeadLetterJobRetryHandler.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/DeadLetterJobRetryHandler.java @@ -6,7 +6,6 @@ import cn.axzo.workflow.core.service.BpmnProcessVariableService; import cn.hutool.json.JSONUtil; import com.alibaba.nacos.common.utils.StringUtils; import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.handler.IJobHandler; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.AllArgsConstructor; import lombok.Data; @@ -26,7 +25,7 @@ import static com.xxl.job.core.biz.model.ReturnT.FAIL_CODE; @Component @RequiredArgsConstructor @Slf4j -public class DeadLetterJobRetryHandler extends IJobHandler { +public class DeadLetterJobRetryHandler { @Resource private BpmnProcessVariableService variableService; @@ -34,7 +33,6 @@ public class DeadLetterJobRetryHandler extends IJobHandler { @Resource private BpmnProcessJobService jobService; - @Override @XxlJob("deadLetterJobRetry") public ReturnT execute(String param) { if (StringUtils.isBlank(param)) { diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/EsIndexOperationJobHandler.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/EsIndexOperationJobHandler.java index f25ebd5f1..b07e23343 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/EsIndexOperationJobHandler.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/EsIndexOperationJobHandler.java @@ -2,9 +2,8 @@ package cn.axzo.workflow.server.xxljob; import cn.axzo.workflow.es.service.EsProcessInstanceService; import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; -import com.xxl.job.core.log.XxlJobLogger; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -19,10 +18,9 @@ import org.springframework.util.StringUtils; @Component @RequiredArgsConstructor @Slf4j -public class EsIndexOperationJobHandler extends IJobHandler { +public class EsIndexOperationJobHandler { private final EsProcessInstanceService esProcessInstanceService; - @Override @XxlJob("esIndexOperation") public ReturnT execute(String param) { if (!StringUtils.hasText(param)) { @@ -38,10 +36,10 @@ public class EsIndexOperationJobHandler extends IJobHandler { */ private void deleteIndex() { log.info("开始删除父子文档索引..."); - XxlJobLogger.log("开始删除父子文档索引..."); + XxlJobHelper.log("开始删除父子文档索引..."); Boolean index = esProcessInstanceService.deleteIndex(); log.info("删除完成. 响应结果: {}", index); - XxlJobLogger.log("删除完成. 响应结果: {}", index); + XxlJobHelper.log("删除完成. 响应结果: {}", index); } /** @@ -49,10 +47,10 @@ public class EsIndexOperationJobHandler extends IJobHandler { */ private void createIndex() { log.info("开始创建父子文档索引..."); - XxlJobLogger.log("开始创建父子文档索引..."); + XxlJobHelper.log("开始创建父子文档索引..."); Boolean index = esProcessInstanceService.createIndex(); // 如果重复调用,避免报错,在创建的逻辑中,内置了判断是否存在指定索引,如果存在则不再创建,直接返回 false. log.info("创建完成. 响应结果: {}", index); - XxlJobLogger.log("创建完成. 响应结果: {}", index); + XxlJobHelper.log("创建完成. 响应结果: {}", index); } } diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OperationDataJobHandler.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OperationDataJobHandler.java index 912c306fd..14a127b58 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OperationDataJobHandler.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OperationDataJobHandler.java @@ -7,9 +7,8 @@ import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst; import cn.axzo.workflow.core.repository.mapper.CommonMapper; import cn.axzo.workflow.core.service.ExtAxHiTaskInstService; import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; -import com.xxl.job.core.log.XxlJobLogger; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -31,21 +30,20 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROV @Component @RequiredArgsConstructor @Slf4j -public class OperationDataJobHandler extends IJobHandler { +public class OperationDataJobHandler { @Resource private CommonMapper commonMapper; @Resource private ExtAxHiTaskInstService extAxHiTaskInstService; - @Override @XxlJob("executeDynamicSql") public ReturnT execute(String s) throws Exception { if (StringUtils.hasText(s)) { - XxlJobLogger.log("执行动态 sql"); + XxlJobHelper.log("执行动态 sql"); List> maps = commonMapper.executeDynamicSQL(s); - XxlJobLogger.log("result: {}", JSON.toJSONString(maps)); + XxlJobHelper.log("result: {}", JSON.toJSONString(maps)); } else { - XxlJobLogger.log("仅修复 extAxTaskInst 表数据"); + XxlJobHelper.log("仅修复 extAxTaskInst 表数据"); repairData(); } return ReturnT.SUCCESS; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OtherProcessInstanceSyncEsJobHandler.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OtherProcessInstanceSyncEsJobHandler.java index 0f2633a45..f33683bdb 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OtherProcessInstanceSyncEsJobHandler.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/OtherProcessInstanceSyncEsJobHandler.java @@ -14,10 +14,8 @@ import cn.hutool.core.util.NumberUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.collect.Lists; import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; -import com.xxl.job.core.log.XxlJobLogger; -import com.xxl.job.core.util.ShardingUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.common.engine.impl.interceptor.CommandExecutor; @@ -52,7 +50,7 @@ import static cn.axzo.workflow.server.controller.delegate.AbstractBpmnTaskAssign @Component @RequiredArgsConstructor @Slf4j -public class OtherProcessInstanceSyncEsJobHandler extends IJobHandler { +public class OtherProcessInstanceSyncEsJobHandler { private final BpmnProcessInstanceForEsService bpmnProcessInstanceForEsService; private final AggregateProcessInstanceService aggregateProcessInstanceService; private final SpringProcessEngineConfiguration springProcessEngineConfiguration; @@ -63,28 +61,28 @@ public class OtherProcessInstanceSyncEsJobHandler extends IJobHandler { @XxlJob("otherProcessInstanceSyncToEs") public ReturnT execute(String param) { // 分片参数 - ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo(); - if (Objects.nonNull(shardingVO) && shardingVO.getTotal() > 1) { - log.info("分片参数:当前分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal()); - XxlJobLogger.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal()); - } + int shardIndex = XxlJobHelper.getShardIndex(); + int shardTotal = XxlJobHelper.getShardTotal(); + log.info("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); + XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); + log.debug("start exec other process instance data sync... "); - XxlJobLogger.log("start exec other process instance data sync... "); + XxlJobHelper.log("start exec other process instance data sync... "); // 同步前的一些额外动作 Date endTime = beforeSync(); if (Objects.isNull(endTime)) { log.info("请先执行“流程实例数据同步 ES 第一步”, Bean Name:completedProcessInstanceSyncToEs。 待其执行完成后再执行该任务!"); - XxlJobLogger.log("请先执行“流程实例数据同步 ES 第一步”, Bean Name:completedProcessInstanceSyncToEs。 待其执行完成后再执行该任务!"); + XxlJobHelper.log("请先执行“流程实例数据同步 ES 第一步”, Bean Name:completedProcessInstanceSyncToEs。 待其执行完成后再执行该任务!"); return ReturnT.FAIL; } // 开始同步流程数据到 ES - DataSyncSummaryDTO summary = doSync(endTime, param, shardingVO.getTotal() > 1 ? shardingVO : null); + DataSyncSummaryDTO summary = doSync(endTime, param, shardIndex, shardTotal); log.info("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); - XxlJobLogger.log("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); + XxlJobHelper.log("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); return ReturnT.SUCCESS; } @@ -99,10 +97,10 @@ public class OtherProcessInstanceSyncEsJobHandler extends IJobHandler { * @param endTime * @param pageSizeStr */ - private DataSyncSummaryDTO doSync(Date endTime, String pageSizeStr, ShardingUtil.ShardingVO shardingVO) { + private DataSyncSummaryDTO doSync(Date endTime, String pageSizeStr, int shardIndex, int shardTotal) { Long totalCount = bpmnProcessInstanceForEsService.queryHistoricProcessInstanceByUnfinishedAndAlterEndTimeTotalCount(endTime); log.info("查询到待同步实例维度数据量: {} 条", totalCount); - XxlJobLogger.log("查询到待同步实例维度数据量: {} 条", totalCount); + XxlJobHelper.log("查询到待同步实例维度数据量: {} 条", totalCount); AtomicInteger pageSize = new AtomicInteger(50); if (StringUtils.hasText(pageSizeStr)) { try { @@ -116,18 +114,18 @@ public class OtherProcessInstanceSyncEsJobHandler extends IJobHandler { .forEach(skipRows -> { log.info("处理进度: {} %, current startRow: {}, endRow: {}", String.format("%.2f", (double) skipRows / totalCount * 100), skipRows, skipRows + pageSize.get()); - XxlJobLogger.log("处理进度: {} %, current startRow: {}, endRow: {}", String.format("%.2f", (double) skipRows / totalCount * 100), + XxlJobHelper.log("处理进度: {} %, current startRow: {}, endRow: {}", String.format("%.2f", (double) skipRows / totalCount * 100), skipRows, skipRows + pageSize.get()); int pageNo = skipRows / pageSize.get() + 1; List instances = bpmnProcessInstanceForEsService.queryHistoricProcessInstanceByUnfinishedAndAlterEndTime(endTime, new Page(pageNo, pageSize.get())); instances.parallelStream().filter(hpi -> { - if (Objects.nonNull(shardingVO)) { + if (shardIndex >= 0 && shardTotal >= 0) { if (hpi.getId().contains("-")) { // 最开始的实例编号是 UUID,无法取模,所以统一交给第一个节点处理 - return shardingVO.getIndex() == 0; + return shardIndex == 0; } - return Long.parseLong(hpi.getId().substring(12)) % shardingVO.getTotal() == shardingVO.getIndex(); + return Long.parseLong(hpi.getId().substring(12)) % shardTotal == shardIndex; } // 如果配置的非分片广播的方式,那么 shardingVO 将是 null return true; diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/SpecifyProcessInstanceSyncEsJobHandler.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/SpecifyProcessInstanceSyncEsJobHandler.java index 44c5efd10..81bfdd5cf 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/SpecifyProcessInstanceSyncEsJobHandler.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/xxljob/SpecifyProcessInstanceSyncEsJobHandler.java @@ -13,9 +13,8 @@ import cn.axzo.workflow.es.service.aggregation.AggregateProcessInstanceService; import cn.hutool.core.util.NumberUtil; import com.google.common.collect.Lists; import com.xxl.job.core.biz.model.ReturnT; -import com.xxl.job.core.handler.IJobHandler; +import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; -import com.xxl.job.core.log.XxlJobLogger; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.history.HistoricProcessInstance; @@ -45,7 +44,7 @@ import static cn.axzo.workflow.server.controller.delegate.AbstractBpmnTaskAssign @Component @RequiredArgsConstructor @Slf4j -public class SpecifyProcessInstanceSyncEsJobHandler extends IJobHandler { +public class SpecifyProcessInstanceSyncEsJobHandler { private final BpmnProcessInstanceForEsService bpmnProcessInstanceForEsService; private final AggregateProcessInstanceService aggregateProcessInstanceService; private final SpringProcessEngineConfiguration springProcessEngineConfiguration; @@ -56,13 +55,13 @@ public class SpecifyProcessInstanceSyncEsJobHandler extends IJobHandler { @XxlJob("specifyProcessInstanceSyncToEs") public ReturnT execute(String processInstanceId) { log.info("Sync specify processInstance id: {}", processInstanceId); - XxlJobLogger.log("Sync specify processInstance id: {}", processInstanceId); + XxlJobHelper.log("Sync specify processInstance id: {}", processInstanceId); // 开始同步流程数据到 ES DataSyncSummaryDTO summary = doSync(processInstanceId); log.info("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); - XxlJobLogger.log("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); + XxlJobHelper.log("Insert Summary: ProcessInstance Count: {}, ProcessTask Count: {}", summary.getProcessInstanceCount(), summary.getProcessTaskCount()); return ReturnT.SUCCESS; } diff --git a/workflow-engine-server/src/main/resources/bootstrap.yml b/workflow-engine-server/src/main/resources/bootstrap.yml index c4f24cc3e..48ac5499a 100644 --- a/workflow-engine-server/src/main/resources/bootstrap.yml +++ b/workflow-engine-server/src/main/resources/bootstrap.yml @@ -30,7 +30,7 @@ spring: nacos: config: # server-addr: ${NACOS_HOST:nacos.mybiwin.top}:${NACOS_PORT:9090} - server-addr: ${NACOS_HOST:https://dev-nacos.axzo.cn}:${NACOS_PORT:443} + server-addr: ${NACOS_HOST:https://dev-nacos-hs.axzo.cn}:${NACOS_PORT:443} file-extension: yaml # namespace: ${NACOS_NAMESPACE_ID:1b5d2a22-b340-4503-8464-7d7fc2059d39} namespace: ${NACOS_NAMESPACE_ID:f82179f1-81a9-41a1-a489-4f9ab5660a6e} @@ -44,7 +44,7 @@ spring: cloud: nacos: config: - server-addr: ${NACOS_HOST:https://dev-nacos.axzo.cn}:${NACOS_PORT:443} + server-addr: ${NACOS_HOST:https://dev-nacos-hs.axzo.cn}:${NACOS_PORT:443} file-extension: yaml namespace: ${NACOS_NAMESPACE_ID:35eada10-9574-4db8-9fea-bc6a4960b6c7} @@ -57,7 +57,7 @@ spring: cloud: nacos: config: - server-addr: ${NACOS_HOST:https://test-nacos.axzo.cn}:${NACOS_PORT:443} + server-addr: ${NACOS_HOST:https://test-nacos-hs.axzo.cn}:${NACOS_PORT:443} file-extension: yaml namespace: ${NACOS_NAMESPACE_ID:f3c0f0d2-bac4-4498-bee7-9c3636b3afdf} diff --git a/workflow-engine-server/src/main/resources/convertorXML.bpmn b/workflow-engine-server/src/main/resources/convertorXML.bpmn index 948e6e7b8..f22d10439 100644 --- a/workflow-engine-server/src/main/resources/convertorXML.bpmn +++ b/workflow-engine-server/src/main/resources/convertorXML.bpmn @@ -1,6 +1,6 @@ - + remark @@ -8,6 +8,10 @@ + + + + @@ -99,8 +103,8 @@ - - + + @@ -109,14 +113,14 @@ - - - - + + + + - - + + @@ -130,7 +134,7 @@ - + @@ -174,7 +178,7 @@ - + ${nrOfInstances == nrOfCompletedInstances} @@ -191,7 +195,6 @@ - @@ -241,7 +244,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -253,7 +256,6 @@ - @@ -303,10 +305,10 @@ ${nrOfInstances == nrOfCompletedInstances} - - - - + + + + @@ -320,7 +322,6 @@ - @@ -381,7 +382,6 @@ - @@ -431,8 +431,8 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + @@ -446,7 +446,6 @@ - @@ -507,7 +506,6 @@ - @@ -557,8 +555,8 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + @@ -572,7 +570,6 @@ - @@ -622,22 +619,23 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + - + - - - + + + + + - @@ -680,18 +678,18 @@ - - + + ${nrOfInstances == nrOfCompletedInstances} - + - - + + @@ -705,7 +703,6 @@ - @@ -755,7 +752,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -769,7 +766,6 @@ - @@ -830,7 +826,6 @@ - @@ -880,7 +875,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -892,7 +887,6 @@ - @@ -942,8 +936,8 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + @@ -955,7 +949,6 @@ - @@ -1016,7 +1009,6 @@ - @@ -1066,7 +1058,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1078,7 +1070,6 @@ - @@ -1128,12 +1119,12 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + - - + + @@ -1147,7 +1138,6 @@ - @@ -1208,7 +1198,6 @@ - @@ -1258,7 +1247,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1270,7 +1259,6 @@ - @@ -1320,8 +1308,8 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + @@ -1335,7 +1323,6 @@ - @@ -1396,7 +1383,6 @@ - @@ -1446,7 +1432,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1458,7 +1444,6 @@ - @@ -1508,7 +1493,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1520,7 +1505,6 @@ - @@ -1570,7 +1554,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1582,7 +1566,6 @@ - @@ -1632,7 +1615,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1644,7 +1627,6 @@ - @@ -1694,8 +1676,8 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + @@ -1707,7 +1689,6 @@ - @@ -1768,7 +1749,6 @@ - @@ -1818,7 +1798,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1830,7 +1810,6 @@ - @@ -1880,7 +1859,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1892,7 +1871,6 @@ - @@ -1942,7 +1920,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -1954,7 +1932,6 @@ - @@ -2004,7 +1981,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -2016,7 +1993,6 @@ - @@ -2066,8 +2042,8 @@ ${nrOfInstances == nrOfCompletedInstances} - - + + @@ -2079,7 +2055,6 @@ - @@ -2129,7 +2104,7 @@ ${nrOfInstances == nrOfCompletedInstances} - + @@ -2141,7 +2116,6 @@ - @@ -2191,451 +2165,453 @@ ${nrOfInstances == nrOfCompletedInstances} - - - - - - - - - - - - + + + + + + + + + + + + - + - + - + - + - + - - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - - - - - + + + + + - - - - - + + + + + - - - - - - - + - - + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - - - - - + + + + + - - - - - + + + + + - - - + + + + + - - - - - + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow-engine-server/src/main/resources/request.json b/workflow-engine-server/src/main/resources/request.json index ebabeea3c..dad1701d3 100644 --- a/workflow-engine-server/src/main/resources/request.json +++ b/workflow-engine-server/src/main/resources/request.json @@ -2776,8 +2776,8 @@ "children": { "id": "node_350811186561", "parentId": "node_347816395827", - "type": "NODE_TASK", - "name": "审批节点", + "type": "NODE_SIGN", + "name": "签署确认人节点", "children": { "id": null, "parentId": null, @@ -2789,10 +2789,11 @@ }, "branches": null, "property": { - "approvalMethod": "autoPassed", - "approverScope": "entWorkspace", - "approverSpecify": "position", - "specifyValue": "", + "approvalMethod": "human", + "signApproverLimit": { + "orgLimit": "LV_0", + "roleLimit": "LEADER", + }, "isMultiTask": true, "isSequential": false, "multiMode": "AND", @@ -10938,6 +10939,13 @@ "supportBatchOperation": true, "userAgreeSignature": true }, + "signConf": { + "signType": "SINGLE", + "signPendingProperty": { + "pendingMessageId": "123", + "viewJson": "{\"templateName\":\"任务审批\",\"templateCode\":\"9684565285b840388f90b6890b3ca60b\",\"category\":\"APPROVAL_PENDING_MESSAGE\",\"title\":\"审批审批\",\"content\":\"您有新任务需要审批,点击查看详情,清及时处理\",\"groupNodeNamePaths\":[\"APP管理端待办/施工管理/任务审批\",\"CMS待办中心/施工管理/任务审批\"],\"status\":\"ENABLE\"}" + } + }, "noticeConf": { "notice": { "noticeMessageId": "", @@ -11382,4 +11390,4 @@ ] }, "id": "202403041453000000001" -} +} \ No newline at end of file diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java index ea8c79b88..e2ed50846 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowCoreService.java @@ -5,11 +5,10 @@ import cn.axzo.workflow.common.util.ThreadUtil; import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC; import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; +import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.Manageable; import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO; import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO; import cn.azxo.framework.common.model.CommonResponse; import io.swagger.v3.oas.annotations.Operation; import org.springframework.validation.annotation.Validated; @@ -18,24 +17,32 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import javax.validation.constraints.NotBlank; -import cn.axzo.workflow.common.annotation.InvokeMode; +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO; 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.BpmnProcessInstanceLogQueryDTO; 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.process.SuperBpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; 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.BpmnProcessInstanceLogVO; 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.NodesByModelVO; import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; +import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PutMapping; @@ -43,23 +50,8 @@ import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import java.util.List; import java.util.Map; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO; -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.BpmnTaskAuditWithFormDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO; -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.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.axzo.workflow.common.model.request.bpmn.task.*; +import cn.axzo.workflow.common.model.response.bpmn.task.*; import javax.validation.constraints.NotEmpty; /** @@ -69,7 +61,7 @@ import javax.validation.constraints.NotEmpty; *

* Auto generation by workflow engine, It cannot be manually modified */ -@org.springframework.cloud.openfeign.FeignClient(name = "workflow-engine-starter-core", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = WorkflowEngineStarterFeignConfiguration.class) +@org.springframework.cloud.openfeign.FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = WorkflowEngineStarterFeignConfiguration.class) public interface WorkflowCoreService { /** @@ -79,6 +71,7 @@ public interface WorkflowCoreService { */ @Deprecated @GetMapping("/api/process/activity/trigger") + @InvokeMode(SYNC) Boolean trigger(@NotBlank(message = "触发 ID 不能为空") @RequestParam String triggerId); /** @@ -104,6 +97,17 @@ public interface WorkflowCoreService { @Operation(summary = "业务节点设置审批人,不支持重复调用设置审批人,需一次性传入所有审批人") Boolean setAssignee(@Validated @RequestBody BpmnActivitySetAssigneeDTO dto); + /** + * 创建流程前的节点列表 + * 用于发起人自选 + * + * @return + */ + @Operation(summary = "创建审批流程前,返回模型节点列表以及节点能否设置审批人") + @PostMapping("/api/process/instance/create/before") + @InvokeMode(SYNC) + List nodesBeforeCreateProcessInstance(@Validated @RequestBody BeforeProcessInstanceCreateDTO dto); + /** * 创建审批流程 * @@ -201,6 +205,66 @@ public interface WorkflowCoreService { @InvokeMode(SYNC) BpmnProcessInstanceLogVO getProcessInstanceLogs(@Validated @RequestBody BpmnProcessInstanceLogQueryDTO dto); + /** + * 根据任务id查询任务状态,按钮详情 + * + * @param taskButtonsSearchDTO 请求参数 + * @return + */ + @Operation(summary = "根据任务id查询任务状态,按钮详情") + @PostMapping("/api/process/instance/task/buttons/find") + @InvokeMode(SYNC) + BpmnTaskButtonVo findProcessSingleTaskButtons(@Validated @RequestBody BpmnTaskButtonSearchDTO taskButtonsSearchDTO); + + /** + * 更新指定流程表单最后一次编辑的内容 + * + * @param dto + * @return + */ + @Operation(summary = "更新指定流程表单最后一次编辑的内容") + @PostMapping("/api/process/instance/form/variable/update") + @InvokeMode(SYNC) + Boolean updateInstanceFormVariables(@Validated @RequestBody FormVariablesUpdateDTO dto); + + /** + * 签署业务流程实例在审批待办中查询使用的文档列表 + * + * @return + */ + @Operation(summary = "签署业务流程实例在审批待办中查询使用的文档列表") + @PostMapping("/api/process/instance/select/doc/list") + @InvokeMode(SYNC) + List processInstanceSelectDocs(@Validated @RequestBody ProcessDocQueryDTO dto); + + /** + * 获取审批人阅读状态 + * + * @return + */ + @Operation(summary = "获取审批人阅读状态") + @PostMapping("/api/process/instance/approver/read/status") + @InvokeMode(SYNC) + List approverReadStatus(@Validated @RequestBody ApproverReadStatusDTO dto); + + /** + * 修改审批人关联文档阅读状态 + */ + @Operation(summary = "修改审批人关联文档阅读状态") + @PostMapping("/api/process/instance/approver/read/status/change") + @InvokeMode(SYNC) + Boolean approveReadStatusChange(@Validated @RequestBody ChangeApproverReadStatusDTO dto); + + /** + * 获取签署业务流程最后替换的文档 fileKey 集合 + * + * @return + */ + @Operation(summary = "获取签署业务流程最后替换的文档 fileKey 集合") + @GetMapping("/api/process/instance/final/docs") + @InvokeMode(SYNC) + List getProcessInstanceFinalDocs(@NotBlank(message = "流程实例 ID 不能为空") @RequestParam(required = false) String processInstanceId); + /** * 同意 * @@ -256,6 +320,15 @@ public interface WorkflowCoreService { @PostMapping("/api/process/task/back") Boolean backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto); + /** + * 用于系统内部操作,跳转到指定节点 + * @param dto 请求参数 + * @return 是否成功 + */ + @Operation(summary = "系统操作回退任务到指定节点") + @PostMapping("/api/process/task/system/back") + Boolean systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto); + /** * 驳回 * diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java index 1cf6dace8..c36b516be 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/api/WorkflowManageService.java @@ -2,97 +2,94 @@ package cn.axzo.workflow.starter.api; import cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration; import cn.axzo.workflow.common.util.ThreadUtil; -import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.ASYNC; -import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.SYNC; import cn.axzo.workflow.client.annotation.WorkflowEngineFeignClient; import cn.axzo.workflow.common.annotation.InvokeMode; import cn.axzo.workflow.common.annotation.Manageable; +import cn.axzo.workflow.common.model.dto.print.PrintFieldDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintFieldQueryDTO; +import cn.azxo.framework.common.model.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.validation.annotation.Validated; +import javax.validation.constraints.NotBlank; +import java.util.List; +import java.util.Map; import cn.axzo.workflow.common.model.request.admin.ProcessAdminCreateDTO; import cn.axzo.workflow.common.model.request.admin.ProcessAdminDeleteDTO; import cn.axzo.workflow.common.model.request.admin.ProcessAdminQueryDTO; import cn.axzo.workflow.common.model.response.admin.ProcessAdminVo; -import cn.azxo.framework.common.model.CommonResponse; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; -import java.util.List; import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutCallbackDTO; import cn.axzo.workflow.common.model.request.bpmn.activity.BpmnActivityTimeoutTriggerDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivitySetAssigneeDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnActivityTriggerDTO; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import javax.validation.constraints.NotBlank; -import cn.axzo.workflow.common.model.request.es.InstanceSearchReqDTO; -import cn.axzo.workflow.common.model.response.BpmPageResult; -import cn.axzo.workflow.common.model.response.es.ProcessInstanceDocumentVO; -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.BpmnProcessInstanceLogQueryDTO; -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.response.bpmn.BatchOperationResultVO; -import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceAdminPageItemVO; -import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessInstanceLogVO; -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 com.fasterxml.jackson.databind.node.ObjectNode; -import org.springframework.web.bind.annotation.PutMapping; -import javax.annotation.Nullable; -import javax.validation.constraints.NotNull; -import java.util.Map; -import cn.axzo.workflow.client.config.CommonFeignConfiguration; -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; -import cn.axzo.workflow.common.model.request.category.CategorySearchDTO; -import cn.axzo.workflow.common.model.request.category.CategoryUpdateDTO; -import cn.axzo.workflow.common.model.response.category.CategoryConfigItemVO; -import cn.axzo.workflow.common.model.response.category.CategoryItemVO; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.PathVariable; import cn.axzo.workflow.common.model.request.form.definition.StartFormSearchDTO; import cn.axzo.workflow.common.model.request.form.instance.FormDetailDTO; import cn.axzo.workflow.common.model.request.form.instance.FormSearchDTO; import cn.axzo.workflow.common.model.response.form.FormVO; import cn.axzo.workflow.common.model.response.form.definition.FormDefinitionVO; import cn.axzo.workflow.common.model.response.form.instance.FormInstanceVO; +import cn.axzo.workflow.common.model.request.es.InstanceSearchReqDTO; +import cn.axzo.workflow.common.model.response.BpmPageResult; +import cn.axzo.workflow.common.model.response.es.ProcessInstanceDocumentVO; +import cn.axzo.workflow.common.model.dto.SignFileDTO; +import cn.axzo.workflow.common.model.dto.SimpleDocDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.BeforeProcessInstanceCreateDTO; +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.BpmnProcessInstanceLogQueryDTO; +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.process.SuperBpmnProcessInstanceCancelDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ChangeApproverReadStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.process.doc.ProcessDocQueryDTO; +import cn.axzo.workflow.common.model.request.form.instance.FormVariablesUpdateDTO; +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.BpmnProcessInstanceLogVO; +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.NodesByModelVO; +import cn.axzo.workflow.common.model.response.bpmn.process.ProcessNodeDetailVO; +import cn.axzo.workflow.common.model.response.bpmn.process.doc.DocPendingVO; +import com.fasterxml.jackson.databind.node.ObjectNode; +import javax.annotation.Nullable; +import javax.validation.constraints.NotNull; +import cn.axzo.workflow.common.model.request.category.*; +import cn.axzo.workflow.common.model.response.category.CategoryConfigItemVO; +import cn.axzo.workflow.common.model.response.category.CategoryGroupVarItemVo; +import cn.axzo.workflow.common.model.response.category.CategoryItemVO; +import org.springframework.web.bind.annotation.*; +import static cn.axzo.workflow.common.enums.RpcInvokeModeEnum.*; import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo; 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; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocByIdDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocCreateDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocOrderDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocResetDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocStatusDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocTenantQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.model.doc.DocUpdateDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigQueryDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.PrintTemplateConfigUpsertDTO; +import cn.axzo.workflow.common.model.request.bpmn.print.RestPrintTemplateConfigDTO; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelDetailVO; import cn.axzo.workflow.common.model.response.bpmn.model.BpmnModelExtVO; +import cn.axzo.workflow.common.model.response.bpmn.model.doc.DocBaseVO; +import cn.axzo.workflow.common.model.response.print.PrintModelDTO; import cn.axzo.workflow.common.model.request.bpmn.RestBpmnProcessVariable; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO; -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.BpmnTaskAuditWithFormDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO; -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.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.axzo.workflow.common.model.request.bpmn.task.*; +import cn.axzo.workflow.common.model.response.bpmn.task.*; import javax.validation.constraints.NotEmpty; import cn.axzo.workflow.common.model.request.bpmn.definition.BpmnProcessDefinitionUpdateDTO; import cn.axzo.workflow.common.model.request.bpmn.process.BpmnProcessDefinitionPageDTO; import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinitionVO; +import static cn.axzo.workflow.common.constant.BpmnConstants.NO_TENANT_ID; /** * Workflow Engine Starter Management Service @@ -101,9 +98,24 @@ import cn.axzo.workflow.common.model.response.bpmn.process.BpmnProcessDefinition *

* Auto generation by workflow engine, It cannot be manually modified */ -@org.springframework.cloud.openfeign.FeignClient(name = "workflow-engine-starter-manage", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = WorkflowEngineStarterFeignConfiguration.class) +@org.springframework.cloud.openfeign.FeignClient(name = "workflow-engine", url = "${axzo.service.workflow-engine:http://workflow-engine:8080}", configuration = WorkflowEngineStarterFeignConfiguration.class) public interface WorkflowManageService { + @Operation(summary = "查询指定审批流程是否能打印,打印开关是否开启,是否存在打印模板") + @GetMapping("/api/print/admin/template/exists") + @InvokeMode(SYNC) + Boolean hasPrintTemplate(@NotBlank(message = "流程实例不能为空") @RequestParam String processInstanceId); + + @Operation(summary = "获取打印模板中可打印的字段") + @PostMapping("/api/print/admin/fields") + @InvokeMode(SYNC) + List getPrintFields(@Validated @RequestBody PrintFieldQueryDTO dto); + + @Operation(summary = "获取指定流程下用于替换打印的相关变量") + @GetMapping("/api/print/admin/field/variables") + @InvokeMode(SYNC) + Map getPrintFieldVariables(@NotBlank(message = "流程实例不能为空") @RequestParam String processInstanceId); + /** * 查询管理员 * @param dto 管理员数据 @@ -187,6 +199,27 @@ public interface WorkflowManageService { @Operation(summary = "设置指定业务节点定时回调") Boolean setTimeoutCallback(@Validated @RequestBody BpmnActivityTimeoutCallbackDTO dto); + @PostMapping("/api/form/admin/form/page") + @InvokeMode(SYNC) + List formPage(@Validated @RequestBody FormSearchDTO dto); + + @PostMapping("/api/form/admin/start/form") + @InvokeMode(SYNC) + FormDefinitionVO getFormDefinition(@Validated @RequestBody StartFormSearchDTO dto); + + /** + * 查询指定审批实例的表单模型和数据 + *

+ * dto 中的 processInstanceId 与 taskId,至少有一个属性有值,一般建议直接使用实例 ID。 + * 当传入 taskId 时,将只查询该任务绑定的表单模型和数据。 + * + * @param dto + * @return + */ + @PostMapping("/api/form/admin/instance/render") + @InvokeMode(SYNC) + FormInstanceVO getFormInstance(@Validated @RequestBody FormDetailDTO dto); + /** * 从 ES 中搜索符合条件的实例纬度数据 * @@ -197,6 +230,10 @@ public interface WorkflowManageService { @InvokeMode(SYNC) BpmPageResult searchInstanceInEs(@Validated @RequestBody InstanceSearchReqDTO dto); + @DeleteMapping("/api/process/instance/super/cancel") + @Manageable + Boolean superCancelProcessInstance(@Validated @RequestBody SuperBpmnProcessInstanceCancelDTO dto); + /** * 查询所有的审批流 * @@ -278,7 +315,7 @@ public interface WorkflowManageService { List getTenantIds(); /** - * 校验指定流程实例下,是否存在指定的审批人 + * 校验指定流程实例下,是否存在指定的审批人正处理待审批 * * @return true 是在当前流程实例中,存在指定的审批人 */ @@ -457,26 +494,22 @@ public interface WorkflowManageService { @InvokeMode(SYNC) Boolean checkCategoryStatus(@RequestParam Long tenantId, @RequestParam String categoryCode); - @PostMapping("/api/form/instance/form/page") + /** + * 查询分类对应的分组以及分组下的变量 + * @param dto 请求参数 + * @return 分组以及分组下的变量 + */ + @PostMapping("/api/process/category/group-with-vars/list") @InvokeMode(SYNC) - List formPage(@Validated @RequestBody FormSearchDTO dto); - - @PostMapping("/api/form/instance/start/form") - @InvokeMode(SYNC) - FormDefinitionVO getFormDefinition(@Validated @RequestBody StartFormSearchDTO dto); + List searchCategoryGroupAndVars(@Validated @RequestBody CategoryGroupVarSearchDto dto); /** - * 查询指定审批实例的表单模型和数据 - *

- * dto 中的 processInstanceId 与 taskId,至少有一个属性有值,一般建议直接使用实例 ID。 - * 当传入 taskId 时,将只查询该任务绑定的表单模型和数据。 - * - * @param dto - * @return + * 新增或者更新分组或者变量 + * @param dto 请求参数 + * @return 是否成功 */ - @PostMapping("/api/form/instance/render") - @InvokeMode(SYNC) - FormInstanceVO getFormInstance(@Validated @RequestBody FormDetailDTO dto); + @PostMapping("/api/process/category/group-with-vars/upsert") + Boolean upsertCategoryGroupAndVars(@Validated @RequestBody CategoryGroupVarUpsertDto dto); /** * 获取流程操作按钮列表 @@ -607,7 +640,20 @@ public interface WorkflowManageService { @Operation(summary = "修改模型状态") @PostMapping("/api/process/model/changeStatus") @InvokeMode(SYNC) - Void changeStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, @NotNull(message = "状态不能为空") @RequestParam Integer status, @RequestParam(required = false) String operator); + Boolean changeStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, @NotNull(message = "状态不能为空") @RequestParam Integer status, @RequestParam(required = false) String operator); + + /** + * 修改模型打印开关状态 + * + * @param modelId + * @param status + * @param operator + * @return + */ + @Operation(summary = "修改模型打印开关状态") + @PostMapping("/api/process/model/print/changeStatus") + @InvokeMode(SYNC) + Boolean changePrintStatus(@NotBlank(message = "模型 ID 不能为空") @RequestParam String modelId, @NotNull(message = "状态不能为空") @RequestParam Integer status, @RequestParam(required = false) String operator); /** * 查询流程模型使用的分类列表 @@ -629,6 +675,176 @@ public interface WorkflowManageService { @InvokeMode(SYNC) List getModelTenantIds(); + /** + * 打印模板配置内容更新保存 + * + * @param dto + * @return + */ + @Operation(summary = "打印模板配置内容更新保存") + @PostMapping("/api/process/model/print/template/upsert") + @InvokeMode(SYNC) + Void printTemplateConfig(@Validated @RequestBody PrintTemplateConfigUpsertDTO dto); + + /** + * 获取打印模板配置内容 + * + * @param dto + * @return + */ + @Operation(summary = "获取打印模板配置内容") + @PostMapping("/api/process/model/print/template/config/query") + @InvokeMode(SYNC) + PrintModelDTO getPrintTemplateConfig(@Validated @RequestBody PrintTemplateConfigQueryDTO dto); + + /** + * 代运营充值的打印模板 + * + * @param dto + * @return + */ + @Operation(summary = "代运营重置打印模板") + @PostMapping(value = "/api/process/model/print/template/config/reset") + @InvokeMode(SYNC) + Boolean resetPrintTemplateConfig(@Validated @RequestBody RestPrintTemplateConfigDTO dto); + + /** + * 搜索文档列表 + * + * @param dto + * @return + */ + @Operation(summary = "搜索文档列表") + @PostMapping(value = "/api/process/model/doc/page") + @InvokeMode(SYNC) + BpmPageResult docPage(@Validated @RequestBody DocSearchDTO dto); + + /** + * 获取指定 docIds 文档列表 + * + * @return + */ + @Operation(summary = "获取指定 docIds 文档列表") + @PostMapping(value = "/api/process/model/doc/ids") + @InvokeMode(SYNC) + List docByIds(@Validated @RequestBody DocByIdDTO dto); + + /** + * 获取指定模板的原始文档列表 + * + * @param dto + * @return + */ + @Operation(summary = "根据业务 ID 获取模型文档列表,自动适配公共模板和代运营") + @PostMapping(value = "/api/process/model/doc/list") + @InvokeMode(SYNC) + List docList(@Validated @RequestBody DocQueryDTO dto); + + /** + * 获取关联 HiPrint 类型文档模板内容 + * + * @param fileRelationId + * @return + */ + @Operation(summary = "获取关联 HiPrint 类型文档模板内容") + @PostMapping(value = "/api/process/model/hi-print/content/get") + @InvokeMode(SYNC) + String getHiPrintContent(@RequestParam String fileRelationId); + + /** + * 添加关联文档 + * + * @return + */ + @Operation(summary = "添加关联文档") + @PutMapping(value = "/api/process/model/doc/create") + @InvokeMode(SYNC) + Boolean createDoc(@Validated @RequestBody DocCreateDTO dto); + + /** + * 修改关联文档 + * + * @return + */ + @Operation(summary = "修改关联文档") + @PostMapping(value = "/api/process/model/doc/update") + @InvokeMode(SYNC) + Boolean updateDoc(@Validated @RequestBody DocUpdateDTO dto); + + /** + * 克隆关联文档 + * + * @param docId + * @return + */ + @Operation(summary = "克隆关联文档") + @PostMapping(value = "/api/process/model/doc/clone") + @InvokeMode(SYNC) + Boolean cloneDoc(@RequestParam("id") Long docId); + + /** + * 删除关联文档 + * + * @return + */ + @Operation(summary = "删除指定文档") + @DeleteMapping(value = "/api/process/model/doc/delete") + @InvokeMode(SYNC) + Boolean deleteDoc(@RequestParam("id") Long docId); + + /** + * 关联文档配置排序 + * + * @param dto + * @return + */ + @Operation(summary = "关联文档配置排序") + @PostMapping(value = "/api/process/model/doc/order") + @InvokeMode(SYNC) + Boolean orderDoc(@Validated @RequestBody DocOrderDTO dto); + + /** + * 重置关联文档 + * + * @param dto + * @return + */ + @Operation(summary = "重置关联文档配置") + @PostMapping(value = "/api/process/model/doc/reset") + @InvokeMode(SYNC) + Boolean resetDoc(@Validated @RequestBody DocResetDTO dto); + + /** + * 设置关联文档的停启用状态 + * + * @param dto + * @return + */ + @Operation(summary = "设置关联文档的停启用状态") + @PostMapping(value = "/api/process/model/doc/status") + @InvokeMode(SYNC) + Boolean statusDoc(@Validated @RequestBody DocStatusDTO dto); + + /** + * 设置关联文档的必选状态 + * + * @return + */ + @Operation(summary = "设置关联文档的必选状态") + @PostMapping(value = "/api/process/model/doc/require") + @InvokeMode(SYNC) + Boolean requireDoc(@Validated @RequestBody DocStatusDTO dto); + + /** + * 特殊的查询设置过关联过文档的工作台 ID 集合 + * + * @return + */ + @Operation(summary = "特殊的查询设置过关联过文档的工作台 ID 集合") + @PostMapping(value = "/api/process/model/has/docs/tenantId") + @InvokeMode(SYNC) + List hasFilesTenantIds(@Validated @RequestBody DocTenantQueryDTO dto); + /** * 为指定流程新增变量 */ @@ -793,7 +1009,7 @@ public interface WorkflowManageService { */ @GetMapping("/api/process/definition/active/getByKey") @InvokeMode(SYNC) - BpmnProcessDefinitionVO getActiveProcessDefinitionByKey(@NotBlank(message = "模型定义KEY不能为空") @RequestParam String key); + BpmnProcessDefinitionVO getActiveProcessDefinitionByKey(@NotBlank(message = "模型定义KEY不能为空") @RequestParam String key, @RequestParam(required = false, defaultValue = NO_TENANT_ID) String tenantId); /** * 挂起/激活流程, diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/common/exception/WorkflowEngineStarterException.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/common/exception/WorkflowEngineStarterException.java index cf4244523..25e7497dd 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/common/exception/WorkflowEngineStarterException.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/common/exception/WorkflowEngineStarterException.java @@ -1,12 +1,14 @@ package cn.axzo.workflow.starter.common.exception; +import cn.axzo.framework.domain.ServiceException; + /** * 流程引擎 starter 的异常类 * * @author wangli * @since 2024/5/21 17:57 */ -public class WorkflowEngineStarterException extends RuntimeException { +public class WorkflowEngineStarterException extends ServiceException { public WorkflowEngineStarterException(Throwable cause) { super(cause); diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/ComplexInvokeClient.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/ComplexInvokeClient.java index 6c6b5bc2f..be27d6ecb 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/ComplexInvokeClient.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/ComplexInvokeClient.java @@ -83,6 +83,10 @@ public class ComplexInvokeClient implements Client { @Override public Response execute(Request request, Request.Options options) throws IOException { log.debug("ComplexInvokeClient execute... Url: {}", request.url()); + if (useMetaFeign(request)) { + log.debug("use meta feign client: {}", request.url()); + return feignClient.execute(request, options); + } RpcInvokeModeEnum currentInvokeModeEnum = getInvokeMode(request); log.debug("[{}] invoke url: {}", currentInvokeModeEnum, request.url()); if (Objects.equals(SYNC, currentInvokeModeEnum)) { @@ -91,6 +95,10 @@ public class ComplexInvokeClient implements Client { return asyncInvoke(request, options); } + private boolean useMetaFeign(Request request) { + return starterProperties.getMetaFeign() && request.requestTemplate().feignTarget().type().getName().contains("cn.axzo.workflow.client.feign"); + } + /** * 发送 RPC 调用动作的 MQ 事件 * @@ -117,12 +125,12 @@ public class ComplexInvokeClient implements Client { Map> headers = request.headers(); headers.forEach((k, v) -> log.debug("ComplexInvokeClient Header: {} = {}", k, v)); return Response.builder() - .status(HttpStatus.OK.value()) - .reason(HttpStatus.OK.getReasonPhrase()) - .headers(headers) - .request(request) - .body(body) - .build(); + .status(HttpStatus.OK.value()) + .reason(HttpStatus.OK.getReasonPhrase()) + .headers(headers) + .request(request) + .body(body) + .build(); } catch (BeansException e) { return feignClient.execute(request, options); } @@ -218,7 +226,7 @@ public class ComplexInvokeClient implements Client { private RpcInvokeModeEnum getInvokeMode(Request request) { Collection invokeModel = request.headers().getOrDefault(STARTER_INVOKE_MODE, - Collections.singletonList(starterProperties.getInvokeMode().name())); + Collections.singletonList(starterProperties.getInvokeMode().name())); if (CollectionUtils.isEmpty(invokeModel)) { return starterProperties.getInvokeMode(); } else if (invokeModel.size() > 1) { diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterDecoder.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterDecoder.java index e3a911ef4..b548f7257 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterDecoder.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterDecoder.java @@ -21,6 +21,8 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_W_E; + /** * Workflow Engine Starter Complex Invoke Client Decoder * @@ -39,13 +41,13 @@ final class WorkflowEngineStarterDecoder implements Decoder { @Override public Object decode(Response response, Type type) throws IOException { if (!isOptional(type)) { - return convert(response, type); + return convertWrapper(response, type); } if (response.status() == 404 || response.status() == 204) { return Optional.empty(); } Type enclosedType = Util.resolveLastTypeParameter(type, Optional.class); - return Optional.ofNullable(convert(response, enclosedType)); + return Optional.ofNullable(convertWrapper(response, enclosedType)); } static boolean isOptional(Type type) { @@ -56,6 +58,14 @@ final class WorkflowEngineStarterDecoder implements Decoder { return parameterizedType.getRawType().equals(Optional.class); } + Object convertWrapper(Response response, Type enclosedType) throws IOException { + if (response.request().headers().containsKey(HEADER_W_E)) { + return delegate.decode(response, enclosedType); + } else { + return convert(response, enclosedType); + } + } + /** * 这里做返回数据的解析,并处理引擎返回的一些正常的业务异常 * diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterFeignConfiguration.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterFeignConfiguration.java index 1d59f0371..794b71bac 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterFeignConfiguration.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/feign/ext/WorkflowEngineStarterFeignConfiguration.java @@ -35,6 +35,7 @@ import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_A import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_HTTP_CLIENT; import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_HTTP_CLIENT_VALUE; import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_SERVER_NAME; +import static cn.axzo.workflow.client.config.WorkflowRequestInterceptor.HEADER_W_E; import static cn.axzo.workflow.common.constant.StarterConstants.ENABLE_MANAGEABLE; import static cn.axzo.workflow.common.constant.StarterConstants.STARTER_INVOKE_MODE; import static java.util.concurrent.TimeUnit.SECONDS; @@ -76,6 +77,9 @@ public class WorkflowEngineStarterFeignConfiguration { // 通过服务端的校验 Target.HardCodedTarget target = (Target.HardCodedTarget) template.feignTarget(); String apiClassPath = target.type().getName(); + if (apiClassPath.contains("cn.axzo.workflow.client")) { + template.header(HEADER_W_E, "true"); + } if (apiClassPath.contains("cn.axzo.workflow.starter.api")) { template.header(HEADER_HTTP_CLIENT, HEADER_HTTP_CLIENT_VALUE); template.header(HEADER_API_VERSION, serviceVersion); diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/AbstractProcessInstanceEventHandler.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/AbstractProcessInstanceEventHandler.java new file mode 100644 index 000000000..8b5120e7a --- /dev/null +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/AbstractProcessInstanceEventHandler.java @@ -0,0 +1,38 @@ +package cn.axzo.workflow.starter.handler; + +import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; +import cn.axzo.workflow.common.model.response.mq.ProcessInstanceDTO; + +/** + * 通过抽象类,将实例的多个结束的方法合并 + *

+ * 注意:Order 的顺序,遵循值越小越优先。(取值范围:Integer. MIN_VALUE - Integer. MAX_VALUE) + * + * @author wangli + * @since 2024/5/27 16:20 + */ +public abstract class AbstractProcessInstanceEventHandler implements ProcessInstanceEventHandler { + + @Override + public void onCompleted(ProcessInstanceDTO dto) { + onFinished(dto, BpmnProcessInstanceResultEnum.APPROVED); + } + + @Override + public void onCancelled(ProcessInstanceDTO dto) { + onFinished(dto, BpmnProcessInstanceResultEnum.CANCELLED); + } + + @Override + public void onRejected(ProcessInstanceDTO dto) { + onFinished(dto, BpmnProcessInstanceResultEnum.REJECTED); + } + + @Override + public void onAborted(ProcessInstanceDTO dto) { + onFinished(dto, BpmnProcessInstanceResultEnum.ABORTED); + } + + public abstract void onFinished(ProcessInstanceDTO dto, BpmnProcessInstanceResultEnum resultEnum); + +} diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/MessageNotificationEventHandler.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/MessageNotificationEventHandler.java index 092d93eda..7899fa51a 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/MessageNotificationEventHandler.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/MessageNotificationEventHandler.java @@ -83,4 +83,10 @@ public interface MessageNotificationEventHandler extends Ordered { default void pushSms(MessagePushDTO dto) { } + /** + * IM 推送 + * @param dto + */ + default void pushIm(MessagePushDTO dto) {} + } diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/ProcessTaskEventHandler.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/ProcessTaskEventHandler.java index 9acdf7970..aa2db9e57 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/ProcessTaskEventHandler.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/handler/ProcessTaskEventHandler.java @@ -56,4 +56,10 @@ public interface ProcessTaskEventHandler extends Ordered { */ default void onDeleted(ProcessTaskDTO dto) { } + + /** + * 用户任务已转交 + * @param dto + */ + default void onTransfer(ProcessTaskDTO dto) {} } diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerNotificationEventListener.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerNotificationEventListener.java index e10c27968..7fdeb435b 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerNotificationEventListener.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerNotificationEventListener.java @@ -66,6 +66,8 @@ public class InnerNotificationEventListener extends AbstractInnerWorkflowListene case PROCESS_PUSH_SMS: consumer = noticeListener::pushSms; break; + case PROCESS_PUSH_IM: + consumer = noticeListener::pushIm; default: log.warn("unknown message event type: {}", type); } diff --git a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerTaskEventListener.java b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerTaskEventListener.java index 8b71e5c19..da5834898 100644 --- a/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerTaskEventListener.java +++ b/workflow-engine-spring-boot-starter/src/main/java/cn/axzo/workflow/starter/mq/broadcast/consumer/InnerTaskEventListener.java @@ -57,6 +57,9 @@ public class InnerTaskEventListener extends AbstractInnerWorkflowListener\r\n" + "Auto generation by workflow engine, It cannot be manually modified"); classOrInterfaceDeclaration.addAndGetAnnotation("org.springframework.cloud.openfeign.FeignClient") - .addPair("name", new StringLiteralExpr("workflow-engine-starter-core")) + .addPair("name", new StringLiteralExpr("workflow-engine")) .addPair("url", new StringLiteralExpr("${axzo.service.workflow-engine:http://workflow-engine:8080}")) .addPair("configuration", new ClassExpr(new ClassOrInterfaceType("WorkflowEngineStarterFeignConfiguration"))); cu.addImport("cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration", false, false); diff --git a/workflow-engine-support/src/main/java/cn/axzo/workflow/support/api/ManageServiceCodeGeneration.java b/workflow-engine-support/src/main/java/cn/axzo/workflow/support/api/ManageServiceCodeGeneration.java index 88223f134..077d798c4 100644 --- a/workflow-engine-support/src/main/java/cn/axzo/workflow/support/api/ManageServiceCodeGeneration.java +++ b/workflow-engine-support/src/main/java/cn/axzo/workflow/support/api/ManageServiceCodeGeneration.java @@ -162,7 +162,7 @@ public class ManageServiceCodeGeneration { "该类是根据 API 动态生成,不同版本可能会开放新的接口,或回收一些旧接口\r\n

\r\n" + "Auto generation by workflow engine, It cannot be manually modified"); classOrInterfaceDeclaration.addAndGetAnnotation("org.springframework.cloud.openfeign.FeignClient") - .addPair("name", new StringLiteralExpr("workflow-engine-starter-manage")) + .addPair("name", new StringLiteralExpr("workflow-engine")) .addPair("url", new StringLiteralExpr("${axzo.service.workflow-engine:http://workflow-engine:8080}")) .addPair("configuration", new ClassExpr(new ClassOrInterfaceType("WorkflowEngineStarterFeignConfiguration"))); cu.addImport("cn.axzo.workflow.starter.feign.ext.WorkflowEngineStarterFeignConfiguration", false, false);