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 8e1bb6fa0..c9af21362 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 @@ -52,7 +52,7 @@ public interface BpmnConstants { String NUMBER_OF_INSTANCES = "nrOfInstances"; String MULTI_INSTANCE_LOOP_COUNTER = "loopCounter"; - + String TASK_COMPLETE_OPERATION_TYPE = "_TASK_COMPLETE_TYPE"; /** * 会签表达式 */ diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonConf.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonConf.java new file mode 100644 index 000000000..fdec1877d --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnButtonConf.java @@ -0,0 +1,86 @@ +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; + +/** + * 流程定义中的按钮配置 + * + * @author wangli + * @since 2023/11/13 13:44 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的按钮管理") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnButtonConf { + + /** + * 发起人的按钮配置信息, JSON 格式 + */ + @ApiModelProperty(value = "发起人的按钮配置信息, JSON 格式", example = "{\"agree\": true, \"reject\": true, \"delegate\": " + + "true, \"cancel\": true, \"withdraw\": true, \"revoke\": true\" }") + @NotBlank(message = "发起人的按钮配置信息不能为空") + private String initiator; + + /** + * 当前审批人的按钮配置信息, JSON 格式 + */ + @ApiModelProperty(value = "当前审批人的按钮配置信息, JSON 格式", example = "{\"agree\": true, \"reject\": true, \"delegate\": " + + "true, \"cancel\": true, \"withdraw\": true, \"revoke\": true\" }") + @NotBlank(message = "当前审批人的按钮配置信息不能为空") + private String current; + + /** + * 历史审批人的按钮配置信息, JSON 格式 + */ + @ApiModelProperty(value = "历史审批人的按钮配置信息, JSON 格式", example = "{\"agree\": true, \"reject\": true, \"delegate\": " + + "true, \"cancel\": true, \"withdraw\": true, \"revoke\": true\" }") + @NotBlank(message = "历史审批人的按钮配置信息不能为空") + private String history; + + /** + * 抄送人的按钮配置信息, JSON 格式 + */ + @ApiModelProperty(value = "抄送人的按钮配置信息, JSON 格式", example = "{\"agree\": true, \"reject\": true, \"delegate\": " + + "true, \"cancel\": true, \"withdraw\": true, \"revoke\": true\" }") + @NotBlank(message = "抄送人的按钮配置信息不能为空") + private String carbonCopy; + + public String getInitiator() { + return initiator; + } + + public void setInitiator(String initiator) { + this.initiator = initiator; + } + + public String getCurrent() { + return current; + } + + public void setCurrent(String current) { + this.current = current; + } + + public String getHistory() { + return history; + } + + public void setHistory(String history) { + this.history = history; + } + + public String getCarbonCopy() { + return carbonCopy; + } + + public void setCarbonCopy(String carbonCopy) { + this.carbonCopy = carbonCopy; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnCondition.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnCondition.java new file mode 100644 index 000000000..0a0dd0869 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnCondition.java @@ -0,0 +1,154 @@ +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 java.util.List; + +/** + * 条件 + * + * @author wangli + * @since 2023/11/13 20:33 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的条件") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnCondition { + + /** + * string(字符串)/number(数字)/radio(单选)/checkbox(复选) + */ + @ApiModelProperty(value = "基础属性:字段类型", notes = "string, number, radio, checkbox", example = "string") + private String filedDateType; + + /** + * 字段 code + */ + @ApiModelProperty(value = "基础属性:字段 code", notes = "字段管理中字段的 Code", example = "fieldCode") + private String fieldCode; + + /** + * 操作符(与字段类型有关) + *

+ * 当字段类型为 string 时, 操作符为 contains, notContains + * 当字段类型为 number 时, 操作符为 eq, ne, gt, ge, lt, le, between + * 当字段类型为 radio 时, 操作符为 eq + * 当字段类型为 checkbox 时, 操作符为 in + */ + @ApiModelProperty(value = "基础属性:操作符", notes = "操作符", example = "eq") + private String operator; + + /** + * 默认的比较值 + */ + @ApiModelProperty(value = "基础属性:默认的比较值", notes = "同时也用于 fieldDateType = radio", example = "1") + private String defaultValue; + + + /** + * 只有 fieldDateType = checkbox 才有值 + */ + @ApiModelProperty(value = "当 fieldDateType = checkbox时, 选中的值") + private List defaultValues; + + /** + * 只有 operator = between 才有值 + */ + @ApiModelProperty(value = "当 fieldDateType = number 并且 operator = between 时, 左侧操作符") + private String leftOperator; + + /** + * 只有 operator = between 才有值 + */ + @ApiModelProperty(value = "当 fieldDateType = number 并且 operator = between 时, 右侧操作符") + private String rightOperator; + + /** + * 只有 operator = between 才有值 + */ + @ApiModelProperty(value = "当 fieldDateType = number 并且 operator = between 时, 左侧比较值") + private String leftValue; + + /** + * 只有 operator = between 才有值 + */ + @ApiModelProperty(value = "当 fieldDateType = number 并且 operator = between 时, 右侧比较值") + private String rightValue; + + public String getFiledDateType() { + return filedDateType; + } + + public void setFiledDateType(String filedDateType) { + this.filedDateType = filedDateType; + } + + public String getFieldCode() { + return fieldCode; + } + + public void setFieldCode(String fieldCode) { + this.fieldCode = fieldCode; + } + + public String getOperator() { + return operator; + } + + public void setOperator(String operator) { + this.operator = operator; + } + + public String getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(String defaultValue) { + this.defaultValue = defaultValue; + } + + public List getDefaultValues() { + return defaultValues; + } + + public void setDefaultValues(List defaultValues) { + this.defaultValues = defaultValues; + } + + public String getLeftOperator() { + return leftOperator; + } + + public void setLeftOperator(String leftOperator) { + this.leftOperator = leftOperator; + } + + public String getRightOperator() { + return rightOperator; + } + + public void setRightOperator(String rightOperator) { + this.rightOperator = rightOperator; + } + + public String getLeftValue() { + return leftValue; + } + + public void setLeftValue(String leftValue) { + this.leftValue = leftValue; + } + + public String getRightValue() { + return rightValue; + } + + public void setRightValue(String rightValue) { + this.rightValue = rightValue; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnConditionGroup.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnConditionGroup.java new file mode 100644 index 000000000..2784cd551 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnConditionGroup.java @@ -0,0 +1,48 @@ +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 java.util.List; + +/** + * 条件组 + *

+ * 目前条件中的所有项都是"并且",所以没有第二个属性,如果以后有"或者"的情况,再加 + * + * @author wangli + * @since 2023/11/13 19:58 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的条件组") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnConditionGroup { + + @ApiModelProperty(value = "条件组") + private List conditions; + + /** + * 条件间模式,默认为"and" + */ + private String mode = "and"; + + public List getConditions() { + return conditions; + } + + public void setConditions(List conditions) { + this.conditions = conditions; + } + + public String getMode() { + return mode; + } + + public void setMode(String mode) { + this.mode = mode; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnFieldConf.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnFieldConf.java new file mode 100644 index 000000000..9c5a8f6a9 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnFieldConf.java @@ -0,0 +1,91 @@ +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.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * 流程定义中的字段管理 + * + * @author wangli + * @since 2023/11/13 13:53 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的字段管理") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnFieldConf { + + /** + * 字段的 code + */ + @ApiModelProperty(value = "字段的 code", example = "fieldCode", notes = "字段的 code 必须唯一") + @NotBlank(message = "字段的 code 不能为空") + private String code; + + /** + * 字段的名称 + */ + @ApiModelProperty(value = "字段的名称", example = "字段名称") + @NotBlank(message = "字段的名称不能为空") + private String name; + + /** + * 字段的类型 + */ + @ApiModelProperty(value = "字段的类型", example = "string", notes = "string, number, date, datetime, boolean, " + + "select, radio, checkbox, textarea, file, image, editor") + @NotBlank(message = "字段的类型不能为空") + private String type; + + /** + * 单选或多选的下拉选择框中的数据, 只有单选或多选的时候才会有值,并且内部的属性不应该为空 + */ + @ApiModelProperty(value = "单选或多选的下拉选择框中的数据") + @Valid + private List options; +} + +/** + * 单选/多选的选项配置 + */ +@Accessors(chain = true) +@Data +@NoArgsConstructor +class BpmnFieldOptionConf { + /** + * 选项的名称 + */ + @ApiModelProperty(value = "选项的名称", example = "选项1") + @NotBlank(message = "选项的名称不能为空") + private String name; + + /** + * 选项的值 + */ + @ApiModelProperty(value = "选项的值", example = "1") + @NotBlank(message = "选项的值不能为空") + private String value; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} 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 cd641c177..5df5b7433 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 @@ -1,11 +1,15 @@ package cn.axzo.workflow.common.model.request.bpmn; import cn.axzo.workflow.common.enums.BpmnFlowNodeMode; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + /** * JSON 版本的 BPMN 协议中 UserTask 节点的属性扩展模型 */ @@ -13,47 +17,71 @@ import lombok.Data; @Data public class BpmnJsonNodeProperty { + //************* 审批方式Start **************// /** - * 发起人节点审批人规则类型 + * 审批方式: human(人工审批), autoPassed(自动通过), autoRejection(自动拒绝), nobody(不审批[仅业务节点可能有该值]) */ - @ApiModelProperty(value = "发起人节点: 发起人规则类型 ASSIGN_USER/POSITION/ROLE") - private String initiatorType; + @ApiModelProperty(value = "任务节点: 审批方式", notes = "human: 人工审批, autoPassed: 自动通过, autoRejection: 自动拒绝, nobody: " + + "不审批[仅业务节点可能有该值]") + @NotBlank(message = "审批方式不能为空") + private String approvalMethod; + //************* 审批方式End **************// + + + //************* 审批人所在范围Start **************// + /** + * 审批人所在范围: EntWorkspace(企业工作台), projectWorkspace(项目工作台), preTaskUserEnt(上节点审批人所在单位) + */ + @ApiModelProperty(value = "任务节点: 审批人所在范围", notes = "EntWorkspace: 企业工作台, projectWorkspace: 项目工作台, preTaskUserEnt:" + + " 上节点审批人所在单位") + private String approverScope; + //************* 审批人所在范围End **************// + + + //************* 审批人指定Start **************// + /** + * 审批人指定: position(指定岗位), role(指定角色), identity(指定身份), initiatorLeader(发起人主管) + */ + @ApiModelProperty(value = "任务节点: 审批人指定", notes = "position: 指定岗位, role: 指定角色, identity: 指定身份, initiatorLeader: " + + "发起人主管") + @NotBlank(message = "审批人指定不能为空") + private String approverSpecify; /** - * 非指定人时, 发起人和任务节点审批人具体的规则值 + * 具体的配置值 */ - @ApiModelProperty(value = "发起人节点/任务节点: 非 ASSIGN_USER 外的规则具体表达式") - private String assigneeExpression; + @ApiModelProperty(value = "任务节点: 审批人指定的具体值") + @NotEmpty(message = "审批人指定的具体值不能为空") + private List specifyValue; + //************* 审批人指定End **************// + + //************* 多人审批时审批方式Start **************// /** - * 是否是多实例Task节点; + * 是否是多实例Task节点, 在安心筑业务中, 所有节点应该都是多实例节点 */ - @ApiModelProperty("任务节点: 是否是多实例Task节点") - private Boolean isMultiTask; + @ApiModelProperty(value = "任务节点: 是否是多实例Task节点", notes = "在安心筑业务中, 所有节点应该都是多实例节点") + private Boolean isMultiTask = true; /** * Task 多实例模式;OR:"或签",AND:"会签" */ @ApiModelProperty(value = "任务节点: 多实例模式;OR:或签,AND:会签") + @NotNull(message = "多实例模式不能为空") private BpmnFlowNodeMode multiMode; + //************* 多人审批时审批方式End **************// + + //************* 审批人为空时Start **************// /** * 审批人为空是否允许自动跳过 */ - @ApiModelProperty(value = "任务节点: 审批人为空是否允许自动跳过") - private Boolean allowSkip = false; + @ApiModelProperty(value = "任务节点: 审批人为空处理方式", notes = "autoPassed: 自动通过, autoSkipped: 自动跳过, transferToAdmin: 转交给管理员") + @NotBlank(message = "审批人为空处理方式不能为空") + private String approverEmptyHandleType; + //************* 审批人为空时的策略End **************// - /** - * 任务节点审批人规则类型 - */ - @ApiModelProperty(value = "任务节点: 审批人规则类型 ASSIGN_USER/POSITION/ROLE") - private String assignedType; - /** - * 指定审批人或发起人时,传入的信息 - */ - @ApiModelProperty(value = "发起人节点/任务节点: 指定审批人") - private BpmnTaskDelegateAssigner assigner; /** * 表单字段权限 @@ -67,23 +95,17 @@ public class BpmnJsonNodeProperty { @ApiModelProperty(value = "发起人节点/任务节点: 按钮权限集合", notes = "后端不做任何解析, 前端给什么样,就返什么样") private String buttonPermission; + + //************* 条件节点Start **************// /** * 条件节点中是否是默认分支,可以都不传; */ @ApiModelProperty(value = "条件节点: 是否是默认条件分支") private Boolean defaultBranch; - /** - * 条件分支的Key - */ - @ApiModelProperty(value = "条件节点: 条件分支的Key", notes = "枢智业务使用的极简条件判断方式") - private String conditionBranchKey; - - /** - * 条件节点当ConditionKey的值是多少,走该分支 - */ - @ApiModelProperty(value = "条件节点: 当ConditionKey的值是多少,走该分支", notes = "枢智业务使用的极简条件判断方式") - private Integer conditionBranchValue; + @ApiModelProperty(value = "条件节点: 条件分组集合") + private List groups; + //************* 条件节点End **************// /** * 发起时使用的表单 key diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnNoticeConf.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnNoticeConf.java new file mode 100644 index 000000000..955456e65 --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnNoticeConf.java @@ -0,0 +1,66 @@ +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; + +/** + * 流程定义中的通知配置 + * + * @author wangli + * @since 2023/11/13 13:38 + */ +@ApiModel("JSON 版本的 BPMN 协议模型中的通知管理") +@Data +@NoArgsConstructor +@Accessors(chain = true) +public class BpmnNoticeConf { + + /** + * 待办消息模板 ID + */ + @ApiModelProperty(value = "待办消息模板 ID") + @NotBlank(message = "待办消息模板 ID 不能为空") + private String pendingMessageId; + + /** + * 通知消息模板 ID + */ + @ApiModelProperty(value = "通知消息模板 ID") + @NotBlank(message = "通知消息模板 ID 不能为空") + private String noticeMessageId; + + /** + * 短信模板 ID + */ + @ApiModelProperty(value = "短信模板 ID") + private String smsId; + + public String getPendingMessageId() { + return pendingMessageId; + } + + public void setPendingMessageId(String pendingMessageId) { + this.pendingMessageId = pendingMessageId; + } + + public String getNoticeMessageId() { + return noticeMessageId; + } + + public void setNoticeMessageId(String noticeMessageId) { + this.noticeMessageId = noticeMessageId; + } + + public String getSmsId() { + return smsId; + } + + public void setSmsId(String smsId) { + this.smsId = smsId; + } +} 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 a2e151699..cc73413c8 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 @@ -1,12 +1,16 @@ package cn.axzo.workflow.common.model.request.bpmn.model; +import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf; +import cn.axzo.workflow.common.model.request.bpmn.BpmnFieldConf; import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonNode; +import cn.axzo.workflow.common.model.request.bpmn.BpmnNoticeConf; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.experimental.Accessors; import org.hibernate.validator.constraints.Length; +import javax.validation.Valid; import javax.validation.constraints.NotBlank; /** @@ -33,7 +37,8 @@ public class BpmnModelCreateDTO { /** * 自定义分类 */ - @ApiModelProperty(value = "自定义分类", notes = "由业务自定义", example = "1") + @ApiModelProperty(value = "自定义分类", notes = "由业务自定义") + @NotBlank(message = "自定义分类不能为空") private String category; /** @@ -45,9 +50,30 @@ public class BpmnModelCreateDTO { /** * 流程的Json 结构 */ - @ApiModelProperty(value = "流程的Json 结构", example = "1") + @ApiModelProperty(value = "流程的 Json 结构") private BpmnJsonNode node; + /** + * 通知管理配置 + */ + @ApiModelProperty(value = "通知管理配置") + @Valid + private BpmnNoticeConf noticeConf; + + /** + * 流程定义的全局默认按钮权限数据 + */ + @ApiModelProperty(value = "流程按钮配置") + @Valid + private BpmnButtonConf buttonConf; + + /** + * 流程定义的全局字段管理数据 + */ + @ApiModelProperty(value = "流程字段配置") + @Valid + private BpmnFieldConf fieldConf; + /** * 租户Id */ diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnErrorCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnErrorCode.java index c2d944d64..46ecd5b88 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnErrorCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/enums/BpmnErrorCode.java @@ -43,7 +43,7 @@ public enum BpmnErrorCode implements IProjectRespCode { PROCESS_OPERATION_PARAM_VALID_ERROR("05001", "参数缺失, 请确保传入了 ID 或 businessKey 对流程实例进行操作"), PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS("05002", "流程取消失败,流程不处于运行中"), PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF("05003", "流程取消失败,该流程不是你发起的"), - PROCESS_INSTANCE_NOT_EXISTS("05004", "流程实例不存在"), + PROCESS_INSTANCE_NOT_EXISTS("05004", "流程实例不存在或已结束"), PROCESS_INSTANCE_ID_NOT_EXISTS("05005", "流程实例【{}】不存在"), PROCESS_INSTANCE_CANCELLED("05006", "流程实例已取消"), // ========== bpmn task 06-001 ========== 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 1c8d7fe18..c36e53b20 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 @@ -77,7 +77,9 @@ public class EngineExecutionStartListener implements ExecutionListener { // UserTask 非多实例 if (!userTask.hasMultiInstanceLoopCharacteristics()) { //TODO by zuoqinbo assigneeIdList.size==0情况 - userTask.setAssignee(assigneeIdList.get(0)); + if (!CollectionUtils.isEmpty(assigneeIdList)) { + userTask.setAssignee(assigneeIdList.get(0)); + } // remove by wangli, 暂时去除审批人变量, 通过上面直接赋值 // execution.setVariable("assigneeUserId", assigneeIdList.get(0)); 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 b3a6208d1..1a02394f8 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 @@ -285,7 +285,8 @@ public class BpmnProcessInstanceServiceImpl implements BpmnProcessInstanceServic public Boolean cancelProcessInstance(BpmnProcessInstanceCancelDTO dto) { HistoricProcessInstance instance = null; if (StringUtils.isNotBlank(dto.getId())) { - instance = getProcessInstance(dto.getId(), dto.getInitiator().getTenantId(), false); + instance = getProcessInstance(dto.getId(), null, false); + // instance = getProcessInstance(dto.getId(), dto.getInitiator().getTenantId(), false); } else if (StringUtils.isNotBlank(dto.getBusinessKey())) { instance = getProcessInstanceByBusinessKey(dto.getBusinessKey(), dto.getInitiator().getTenantId(), false); } else { diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java index 97821e0f3..a382dc1c8 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java @@ -1,8 +1,13 @@ package cn.axzo.workflow.core.service.impl; -import cn.axzo.workflow.common.enums.BpmnCountersignType; import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; -import cn.axzo.workflow.common.model.request.bpmn.task.*; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAssigneeDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; +import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceGroupVO; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO; @@ -72,8 +77,8 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_SPECIFY_NE import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT; 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.TASK_COMPLETE_OPERATION_TYPE; 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.PROCESSING; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.valueOfStatus; @@ -204,7 +209,7 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { NativeHistoricTaskInstanceQuery nativeQuery = historyService.createNativeHistoricTaskInstanceQuery(); String tableName = managementService.getTableName(HistoricTaskInstance.class); baseQuerySql.append("SELECT a.* FROM ").append(tableName).append(" a JOIN " + - "ACT_HI_PROCINST b") + "ACT_HI_PROCINST b") .append(" ON b.PROC_INST_ID_ = a.PROC_INST_ID_"); if (StringUtils.hasLength(dto.getTenantId())) { @@ -286,7 +291,7 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { Task task = checkTask(dto.getApprover().getTenantId(), dto.getApprover().buildAssigneeId(), dto.getTaskId()); // 校验流程实例存在 HistoricProcessInstance instance = processInstanceService.getProcessInstance( - task.getProcessInstanceId(), dto.getApprover().getTenantId(), true); + task.getProcessInstanceId(), null, true); if (instance == null) { throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS); } @@ -374,7 +379,9 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { query.taskTenantId(tenantId); } HistoricProcessInstanceQuery instanceQuery = - historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId); + historyService.createHistoricProcessInstanceQuery() + .includeProcessVariables() + .processInstanceId(processInstanceId); if (StringUtils.hasLength(tenantId)) { instanceQuery.processInstanceTenantId(tenantId); } @@ -394,20 +401,16 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { Map variableInstanceMap = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list() .stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, - Function.identity(), (s, t) -> s)); + Function.identity(), (s, t) -> s)); Set taskDefinitionKeys = new HashSet<>(); int count = 0; - BpmnProcessInstanceResultEnum finalBusinessStatus = valueOfStatus(instance.getBusinessStatus()); + BpmnProcessInstanceResultEnum processBusinessStatus = valueOfStatus(instance.getBusinessStatus()); for (BpmnHistoricTaskInstanceVO vo : vos) { - if (count == 0 || taskDefinitionKeys.contains(vo.getTaskDefinitionKey())) { - vo.setResult(finalBusinessStatus); - if (Objects.nonNull(vo.getEndTime()) && !Objects.equals(finalBusinessStatus, REJECTED) - && !Objects.equals(finalBusinessStatus, CANCELLED)) { - vo.setResult(APPROVED); - } - } else { - vo.setResult(APPROVED); + vo.setResult(processBusinessStatus); + if (Objects.nonNull(vo.getEndTime())) { + // 只有拒绝时, 为指定的 taskId 设置过拒绝变量 + vo.setResult((BpmnProcessInstanceResultEnum) instance.getProcessVariables().getOrDefault(TASK_COMPLETE_OPERATION_TYPE + vo.getTaskId(), APPROVED)); } taskDefinitionKeys.add(vo.getTaskDefinitionKey()); List taskComments = commentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList()); @@ -482,9 +485,20 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { List vos = bpmnTaskConverter.toVos(query.list()); List snapshotTaskIds = vos.stream().map(i -> INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + i.getTaskId()).collect(Collectors.toList()); + ProcessInstance processInstance = + runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(processInstance)) { + throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS); + } Map instanceMap = runtimeService.getVariableInstances(processInstanceId, snapshotTaskIds); - vos.forEach(i -> i.setAssigner((BpmnTaskDelegateAssigner) instanceMap.get(INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + i.getTaskId()).getValue())); + vos.forEach(i -> { + VariableInstance variableInstance = + instanceMap.get(INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + i.getTaskId()); + if (!Objects.isNull(variableInstance)) { + i.setAssigner((BpmnTaskDelegateAssigner) variableInstance.getValue()); + } + }); return vos; } @@ -497,13 +511,11 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { * @param taskId 任务ID */ private Task checkTask(String tenantId, String assignee, String taskId) { - - Task task = getTask(taskId, assignee, tenantId); - if (Objects.nonNull(task) && !Objects.equals(assignee, task.getAssignee())) { - throw new WorkflowEngineException(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF); - } + Task task = getTask(taskId, null, null); if (Objects.isNull(task)) { throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS); + } else if (!Objects.equals(assignee, task.getAssignee()) && !Objects.equals(tenantId, task.getTenantId())) { + throw new WorkflowEngineException(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF); } return task; } diff --git a/workflow-engine-core/src/main/resources/test.bpmn20.xml b/workflow-engine-core/src/main/resources/test.bpmn20.xml new file mode 100644 index 000000000..b8e5d06b8 --- /dev/null +++ b/workflow-engine-core/src/main/resources/test.bpmn20.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BpmnDefaultTaskDelegateImpl.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BpmnDefaultTaskDelegateImpl.java index 537b8ded3..a53990fac 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BpmnDefaultTaskDelegateImpl.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/delegate/BpmnDefaultTaskDelegateImpl.java @@ -94,6 +94,27 @@ public class BpmnDefaultTaskDelegateImpl implements BpmnTaskDelegate { assigners.add(assigner2); } } + + if (CollectionUtils.isEmpty(assigners) && Objects.equals("69", delegateTask.getTenantId())) { + if (Objects.equals("NODE1639041346796_0.5413743892974803_0", delegateTask.getTaskDefinitionKey())) { + BpmnTaskDelegateAssigner assigner1 = new BpmnTaskDelegateAssigner(); + assigner1.setAssignee("1"); + assigner1.setAssigneeType("5"); + assigner1.setAssignerName("或签人1"); + assigner1.setPersonId("100001"); + assigner1.setTenantId("296"); + assigners.add(assigner1); + } + if (Objects.equals("NODE1687933854432_0.45762305710855467_1", delegateTask.getTaskDefinitionKey())) { + BpmnTaskDelegateAssigner assigner1 = new BpmnTaskDelegateAssigner(); + assigner1.setAssignee("1"); + assigner1.setAssigneeType("5"); + assigner1.setAssignerName("或签人1"); + assigner1.setPersonId("100001"); + assigner1.setTenantId("296"); + assigners.add(assigner1); + } + } return assigners; } } diff --git a/workflow-engine-server/src/main/resources/bootstrap.yml b/workflow-engine-server/src/main/resources/bootstrap.yml index ba3bc7d89..fd86ec1a4 100644 --- a/workflow-engine-server/src/main/resources/bootstrap.yml +++ b/workflow-engine-server/src/main/resources/bootstrap.yml @@ -32,7 +32,7 @@ spring: server-addr: ${NACOS_HOST:https://dev-nacos.axzo.cn}:${NACOS_PORT:443} file-extension: yaml # namespace: ${NACOS_NAMESPACE_ID:1b5d2a22-b340-4503-8464-7d7fc2059d39} - namespace: ${NACOS_NAMESPACE_ID:35eada10-9574-4db8-9fea-bc6a4960b6c7} + namespace: ${NACOS_NAMESPACE_ID:f82179f1-81a9-41a1-a489-4f9ab5660a6e} --- #开发环境