feat: 归档L03与L09审批发布能力
- 新增统一审批中心与审批管理页面,支持流程配置、审批详情与角色/用户审批对象 - 接入聊天助手、知识库、工作流的发布与删除审批,并补齐发布态校验与快照展示
This commit is contained in:
37
easyflow-modules/easyflow-module-approval/pom.xml
Normal file
37
easyflow-modules/easyflow-module-approval/pom.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-modules</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<name>easyflow-module-approval</name>
|
||||
<artifactId>easyflow-module-approval</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring-boot3-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-base</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-satoken</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-common-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tech.easyflow</groupId>
|
||||
<artifactId>easyflow-module-system</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,35 @@
|
||||
package tech.easyflow.approval.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 提交审批动作注解。
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ApprovalAction {
|
||||
|
||||
/**
|
||||
* 资源类型。
|
||||
*
|
||||
* @return 资源类型编码
|
||||
*/
|
||||
String resourceType();
|
||||
|
||||
/**
|
||||
* 动作类型。
|
||||
*
|
||||
* @return 动作类型编码
|
||||
*/
|
||||
String actionType();
|
||||
|
||||
/**
|
||||
* 资源 ID 的 SpEL 表达式。
|
||||
*
|
||||
* @return SpEL 表达式
|
||||
*/
|
||||
String idExpr();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package tech.easyflow.approval.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 已发布访问门禁注解。
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequirePublishedAccess {
|
||||
|
||||
/**
|
||||
* 资源类型。
|
||||
*
|
||||
* @return 资源类型编码
|
||||
*/
|
||||
String resourceType();
|
||||
|
||||
/**
|
||||
* 资源标识的 SpEL 表达式。
|
||||
*
|
||||
* @return 标识表达式
|
||||
*/
|
||||
String idExpr();
|
||||
|
||||
/**
|
||||
* 拒绝访问时的提示文案。
|
||||
*
|
||||
* @return 错误提示
|
||||
*/
|
||||
String denyMessage() default "资源尚未发布";
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package tech.easyflow.approval.aspect;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.easyflow.approval.annotation.ApprovalAction;
|
||||
import tech.easyflow.approval.service.ApprovalActionFacade;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 提审动作切面。
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class ApprovalActionAspect {
|
||||
|
||||
private final ApprovalActionFacade approvalActionFacade;
|
||||
private final ExpressionParser expressionParser = new SpelExpressionParser();
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
public ApprovalActionAspect(ApprovalActionFacade approvalActionFacade) {
|
||||
this.approvalActionFacade = approvalActionFacade;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截提审方法,先执行资源校验,再统一发起审批。
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param approvalAction 注解
|
||||
* @return 审批实例 ID
|
||||
* @throws Throwable 执行异常
|
||||
*/
|
||||
@Around("@annotation(approvalAction)")
|
||||
public Object doAround(ProceedingJoinPoint joinPoint, ApprovalAction approvalAction) throws Throwable {
|
||||
Object identifier = resolveIdentifier(joinPoint, approvalAction.idExpr());
|
||||
BigInteger resourceId = identifier == null ? null : new BigInteger(String.valueOf(identifier));
|
||||
joinPoint.proceed();
|
||||
return approvalActionFacade.submit(
|
||||
approvalAction.resourceType(),
|
||||
resourceId,
|
||||
approvalAction.actionType(),
|
||||
SaTokenUtil.getLoginAccount().getId()
|
||||
);
|
||||
}
|
||||
|
||||
private Object resolveIdentifier(ProceedingJoinPoint joinPoint, String idExpr) {
|
||||
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
|
||||
MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(
|
||||
joinPoint.getTarget(),
|
||||
method,
|
||||
joinPoint.getArgs(),
|
||||
parameterNameDiscoverer
|
||||
);
|
||||
return expressionParser.parseExpression(idExpr).getValue(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package tech.easyflow.approval.aspect;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.context.expression.MethodBasedEvaluationContext;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tech.easyflow.approval.annotation.RequirePublishedAccess;
|
||||
import tech.easyflow.approval.service.ApprovalActionFacade;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 已发布访问门禁切面。
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class RequirePublishedAccessAspect {
|
||||
|
||||
private final ApprovalActionFacade approvalActionFacade;
|
||||
private final ExpressionParser expressionParser = new SpelExpressionParser();
|
||||
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
|
||||
|
||||
public RequirePublishedAccessAspect(ApprovalActionFacade approvalActionFacade) {
|
||||
this.approvalActionFacade = approvalActionFacade;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发布态门禁校验。
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param requirePublishedAccess 注解
|
||||
* @return 原方法返回值
|
||||
* @throws Throwable 执行异常
|
||||
*/
|
||||
@Around("@annotation(requirePublishedAccess)")
|
||||
public Object doAround(ProceedingJoinPoint joinPoint,
|
||||
RequirePublishedAccess requirePublishedAccess) throws Throwable {
|
||||
Object identifier = resolveIdentifier(joinPoint, requirePublishedAccess.idExpr());
|
||||
approvalActionFacade.assertPublishedAccess(
|
||||
requirePublishedAccess.resourceType(),
|
||||
identifier,
|
||||
requirePublishedAccess.denyMessage()
|
||||
);
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
|
||||
private Object resolveIdentifier(ProceedingJoinPoint joinPoint, String idExpr) {
|
||||
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
|
||||
MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(
|
||||
joinPoint.getTarget(),
|
||||
method,
|
||||
joinPoint.getArgs(),
|
||||
parameterNameDiscoverer
|
||||
);
|
||||
return expressionParser.parseExpression(idExpr).getValue(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package tech.easyflow.approval.config;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
|
||||
/**
|
||||
* 审批模块配置。
|
||||
*/
|
||||
@MapperScan("tech.easyflow.approval.mapper")
|
||||
@AutoConfiguration
|
||||
public class ApprovalModuleConfig {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tech.easyflow.approval.entity;
|
||||
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import tech.easyflow.approval.entity.base.ApprovalFlowBase;
|
||||
|
||||
/**
|
||||
* 审批流程实体。
|
||||
*/
|
||||
@Table("tb_approval_flow")
|
||||
public class ApprovalFlow extends ApprovalFlowBase {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tech.easyflow.approval.entity;
|
||||
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import tech.easyflow.approval.entity.base.ApprovalFlowScopeBase;
|
||||
|
||||
/**
|
||||
* 审批流程范围实体。
|
||||
*/
|
||||
@Table("tb_approval_flow_scope")
|
||||
public class ApprovalFlowScope extends ApprovalFlowScopeBase {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tech.easyflow.approval.entity;
|
||||
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import tech.easyflow.approval.entity.base.ApprovalFlowStepBase;
|
||||
|
||||
/**
|
||||
* 审批流程步骤实体。
|
||||
*/
|
||||
@Table("tb_approval_flow_step")
|
||||
public class ApprovalFlowStep extends ApprovalFlowStepBase {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tech.easyflow.approval.entity;
|
||||
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import tech.easyflow.approval.entity.base.ApprovalInstanceBase;
|
||||
|
||||
/**
|
||||
* 审批实例实体。
|
||||
*/
|
||||
@Table("tb_approval_instance")
|
||||
public class ApprovalInstance extends ApprovalInstanceBase {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tech.easyflow.approval.entity;
|
||||
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import tech.easyflow.approval.entity.base.ApprovalLogBase;
|
||||
|
||||
/**
|
||||
* 审批日志实体。
|
||||
*/
|
||||
@Table("tb_approval_log")
|
||||
public class ApprovalLog extends ApprovalLogBase {
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tech.easyflow.approval.entity;
|
||||
|
||||
import com.mybatisflex.annotation.Table;
|
||||
import tech.easyflow.approval.entity.base.ApprovalTaskBase;
|
||||
|
||||
/**
|
||||
* 审批任务实体。
|
||||
*/
|
||||
@Table("tb_approval_task")
|
||||
public class ApprovalTask extends ApprovalTaskBase {
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package tech.easyflow.approval.entity.base;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 审批流程主表基础字段。
|
||||
*/
|
||||
public class ApprovalFlowBase implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
|
||||
private BigInteger id;
|
||||
|
||||
@Column(comment = "流程名称")
|
||||
private String name;
|
||||
|
||||
@Column(comment = "资源类型")
|
||||
private String resourceType;
|
||||
|
||||
@Column(comment = "动作类型")
|
||||
private String actionType;
|
||||
|
||||
@Column(comment = "优先级")
|
||||
private Integer priority;
|
||||
|
||||
@Column(comment = "流程状态")
|
||||
private String status;
|
||||
|
||||
@Column(comment = "流程版本")
|
||||
private Integer version;
|
||||
|
||||
@Column(comment = "备注")
|
||||
private String remark;
|
||||
|
||||
@Column(comment = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@Column(comment = "创建者")
|
||||
private BigInteger createdBy;
|
||||
|
||||
@Column(comment = "修改时间")
|
||||
private Date modified;
|
||||
|
||||
@Column(comment = "修改者")
|
||||
private BigInteger modifiedBy;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public Integer getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(Integer priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public BigInteger getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(BigInteger createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public BigInteger getModifiedBy() {
|
||||
return modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedBy(BigInteger modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package tech.easyflow.approval.entity.base;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 审批流程范围基础字段。
|
||||
*/
|
||||
public class ApprovalFlowScopeBase implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
|
||||
private BigInteger id;
|
||||
|
||||
@Column(comment = "流程ID")
|
||||
private BigInteger flowId;
|
||||
|
||||
@Column(comment = "范围类型")
|
||||
private String scopeType;
|
||||
|
||||
@Column(comment = "范围值")
|
||||
private BigInteger scopeValue;
|
||||
|
||||
@Column(comment = "是否包含子节点")
|
||||
private Integer includeChildren;
|
||||
|
||||
@Column(comment = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@Column(comment = "创建者")
|
||||
private BigInteger createdBy;
|
||||
|
||||
@Column(comment = "修改时间")
|
||||
private Date modified;
|
||||
|
||||
@Column(comment = "修改者")
|
||||
private BigInteger modifiedBy;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BigInteger getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public void setFlowId(BigInteger flowId) {
|
||||
this.flowId = flowId;
|
||||
}
|
||||
|
||||
public String getScopeType() {
|
||||
return scopeType;
|
||||
}
|
||||
|
||||
public void setScopeType(String scopeType) {
|
||||
this.scopeType = scopeType;
|
||||
}
|
||||
|
||||
public BigInteger getScopeValue() {
|
||||
return scopeValue;
|
||||
}
|
||||
|
||||
public void setScopeValue(BigInteger scopeValue) {
|
||||
this.scopeValue = scopeValue;
|
||||
}
|
||||
|
||||
public Integer getIncludeChildren() {
|
||||
return includeChildren;
|
||||
}
|
||||
|
||||
public void setIncludeChildren(Integer includeChildren) {
|
||||
this.includeChildren = includeChildren;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public BigInteger getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(BigInteger createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public BigInteger getModifiedBy() {
|
||||
return modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedBy(BigInteger modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package tech.easyflow.approval.entity.base;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 审批流程步骤基础字段。
|
||||
*/
|
||||
public class ApprovalFlowStepBase implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
|
||||
private BigInteger id;
|
||||
|
||||
@Column(comment = "流程ID")
|
||||
private BigInteger flowId;
|
||||
|
||||
@Column(comment = "步骤顺序")
|
||||
private Integer stepNo;
|
||||
|
||||
@Column(comment = "步骤名称")
|
||||
private String stepName;
|
||||
|
||||
@Column(comment = "审批对象类型")
|
||||
private String assigneeType;
|
||||
|
||||
@Column(comment = "审批对象ID")
|
||||
private BigInteger assigneeTargetId;
|
||||
|
||||
@Column(comment = "审批对象编码")
|
||||
private String assigneeTargetCode;
|
||||
|
||||
@Column(comment = "审批对象名称")
|
||||
private String assigneeTargetName;
|
||||
|
||||
@Column(comment = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@Column(comment = "创建者")
|
||||
private BigInteger createdBy;
|
||||
|
||||
@Column(comment = "修改时间")
|
||||
private Date modified;
|
||||
|
||||
@Column(comment = "修改者")
|
||||
private BigInteger modifiedBy;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BigInteger getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public void setFlowId(BigInteger flowId) {
|
||||
this.flowId = flowId;
|
||||
}
|
||||
|
||||
public Integer getStepNo() {
|
||||
return stepNo;
|
||||
}
|
||||
|
||||
public void setStepNo(Integer stepNo) {
|
||||
this.stepNo = stepNo;
|
||||
}
|
||||
|
||||
public String getStepName() {
|
||||
return stepName;
|
||||
}
|
||||
|
||||
public void setStepName(String stepName) {
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
public String getAssigneeType() {
|
||||
return assigneeType;
|
||||
}
|
||||
|
||||
public void setAssigneeType(String assigneeType) {
|
||||
this.assigneeType = assigneeType;
|
||||
}
|
||||
|
||||
public BigInteger getAssigneeTargetId() {
|
||||
return assigneeTargetId;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetId(BigInteger assigneeTargetId) {
|
||||
this.assigneeTargetId = assigneeTargetId;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetCode() {
|
||||
return assigneeTargetCode;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetCode(String assigneeTargetCode) {
|
||||
this.assigneeTargetCode = assigneeTargetCode;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetName() {
|
||||
return assigneeTargetName;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetName(String assigneeTargetName) {
|
||||
this.assigneeTargetName = assigneeTargetName;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public BigInteger getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(BigInteger createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public BigInteger getModifiedBy() {
|
||||
return modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedBy(BigInteger modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package tech.easyflow.approval.entity.base;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.core.handler.FastjsonTypeHandler;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 审批实例基础字段。
|
||||
*/
|
||||
public class ApprovalInstanceBase implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
|
||||
private BigInteger id;
|
||||
|
||||
@Column(comment = "流程ID")
|
||||
private BigInteger flowId;
|
||||
|
||||
@Column(comment = "流程版本")
|
||||
private Integer flowVersion;
|
||||
|
||||
@Column(comment = "资源类型")
|
||||
private String resourceType;
|
||||
|
||||
@Column(comment = "资源ID")
|
||||
private BigInteger resourceId;
|
||||
|
||||
@Column(comment = "动作类型")
|
||||
private String actionType;
|
||||
|
||||
@Column(comment = "实例状态")
|
||||
private String status;
|
||||
|
||||
@Column(comment = "当前步骤序号")
|
||||
private Integer currentStepNo;
|
||||
|
||||
@Column(typeHandler = FastjsonTypeHandler.class, comment = "审批快照")
|
||||
private Map<String, Object> snapshotJson;
|
||||
|
||||
@Column(comment = "审批摘要")
|
||||
private String summary;
|
||||
|
||||
@Column(comment = "申请人ID")
|
||||
private BigInteger applicantId;
|
||||
|
||||
@Column(comment = "提交时间")
|
||||
private Date submittedAt;
|
||||
|
||||
@Column(comment = "完成时间")
|
||||
private Date finishedAt;
|
||||
|
||||
@Column(comment = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@Column(comment = "创建者")
|
||||
private BigInteger createdBy;
|
||||
|
||||
@Column(comment = "修改时间")
|
||||
private Date modified;
|
||||
|
||||
@Column(comment = "修改者")
|
||||
private BigInteger modifiedBy;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BigInteger getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public void setFlowId(BigInteger flowId) {
|
||||
this.flowId = flowId;
|
||||
}
|
||||
|
||||
public Integer getFlowVersion() {
|
||||
return flowVersion;
|
||||
}
|
||||
|
||||
public void setFlowVersion(Integer flowVersion) {
|
||||
this.flowVersion = flowVersion;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public BigInteger getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(BigInteger resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getCurrentStepNo() {
|
||||
return currentStepNo;
|
||||
}
|
||||
|
||||
public void setCurrentStepNo(Integer currentStepNo) {
|
||||
this.currentStepNo = currentStepNo;
|
||||
}
|
||||
|
||||
public Map<String, Object> getSnapshotJson() {
|
||||
return snapshotJson;
|
||||
}
|
||||
|
||||
public void setSnapshotJson(Map<String, Object> snapshotJson) {
|
||||
this.snapshotJson = snapshotJson;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public BigInteger getApplicantId() {
|
||||
return applicantId;
|
||||
}
|
||||
|
||||
public void setApplicantId(BigInteger applicantId) {
|
||||
this.applicantId = applicantId;
|
||||
}
|
||||
|
||||
public Date getSubmittedAt() {
|
||||
return submittedAt;
|
||||
}
|
||||
|
||||
public void setSubmittedAt(Date submittedAt) {
|
||||
this.submittedAt = submittedAt;
|
||||
}
|
||||
|
||||
public Date getFinishedAt() {
|
||||
return finishedAt;
|
||||
}
|
||||
|
||||
public void setFinishedAt(Date finishedAt) {
|
||||
this.finishedAt = finishedAt;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public BigInteger getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(BigInteger createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public BigInteger getModifiedBy() {
|
||||
return modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedBy(BigInteger modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package tech.easyflow.approval.entity.base;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
import com.mybatisflex.core.handler.FastjsonTypeHandler;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 审批日志基础字段。
|
||||
*/
|
||||
public class ApprovalLogBase implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
|
||||
private BigInteger id;
|
||||
|
||||
@Column(comment = "审批实例ID")
|
||||
private BigInteger instanceId;
|
||||
|
||||
@Column(comment = "事件类型")
|
||||
private String eventType;
|
||||
|
||||
@Column(comment = "操作人ID")
|
||||
private BigInteger operatorId;
|
||||
|
||||
@Column(typeHandler = FastjsonTypeHandler.class, comment = "事件载荷")
|
||||
private Map<String, Object> payloadJson;
|
||||
|
||||
@Column(comment = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@Column(comment = "创建者")
|
||||
private BigInteger createdBy;
|
||||
|
||||
@Column(comment = "修改时间")
|
||||
private Date modified;
|
||||
|
||||
@Column(comment = "修改者")
|
||||
private BigInteger modifiedBy;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BigInteger getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setInstanceId(BigInteger instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public String getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public void setEventType(String eventType) {
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
public BigInteger getOperatorId() {
|
||||
return operatorId;
|
||||
}
|
||||
|
||||
public void setOperatorId(BigInteger operatorId) {
|
||||
this.operatorId = operatorId;
|
||||
}
|
||||
|
||||
public Map<String, Object> getPayloadJson() {
|
||||
return payloadJson;
|
||||
}
|
||||
|
||||
public void setPayloadJson(Map<String, Object> payloadJson) {
|
||||
this.payloadJson = payloadJson;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public BigInteger getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(BigInteger createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public BigInteger getModifiedBy() {
|
||||
return modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedBy(BigInteger modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package tech.easyflow.approval.entity.base;
|
||||
|
||||
import com.mybatisflex.annotation.Column;
|
||||
import com.mybatisflex.annotation.Id;
|
||||
import com.mybatisflex.annotation.KeyType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 审批任务基础字段。
|
||||
*/
|
||||
public class ApprovalTaskBase implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
|
||||
private BigInteger id;
|
||||
|
||||
@Column(comment = "审批实例ID")
|
||||
private BigInteger instanceId;
|
||||
|
||||
@Column(comment = "步骤序号")
|
||||
private Integer stepNo;
|
||||
|
||||
@Column(comment = "任务状态")
|
||||
private String status;
|
||||
|
||||
@Column(comment = "指派角色编码")
|
||||
private String assigneeRoleCode;
|
||||
|
||||
@Column(comment = "审批对象类型")
|
||||
private String assigneeType;
|
||||
|
||||
@Column(comment = "审批对象ID")
|
||||
private BigInteger assigneeTargetId;
|
||||
|
||||
@Column(comment = "审批对象编码")
|
||||
private String assigneeTargetCode;
|
||||
|
||||
@Column(comment = "审批对象名称")
|
||||
private String assigneeTargetName;
|
||||
|
||||
@Column(comment = "处理人ID")
|
||||
private BigInteger actedBy;
|
||||
|
||||
@Column(comment = "处理时间")
|
||||
private Date actedAt;
|
||||
|
||||
@Column(comment = "处理意见")
|
||||
private String comment;
|
||||
|
||||
@Column(comment = "创建时间")
|
||||
private Date created;
|
||||
|
||||
@Column(comment = "创建者")
|
||||
private BigInteger createdBy;
|
||||
|
||||
@Column(comment = "修改时间")
|
||||
private Date modified;
|
||||
|
||||
@Column(comment = "修改者")
|
||||
private BigInteger modifiedBy;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BigInteger getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setInstanceId(BigInteger instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public Integer getStepNo() {
|
||||
return stepNo;
|
||||
}
|
||||
|
||||
public void setStepNo(Integer stepNo) {
|
||||
this.stepNo = stepNo;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getAssigneeRoleCode() {
|
||||
return assigneeRoleCode;
|
||||
}
|
||||
|
||||
public void setAssigneeRoleCode(String assigneeRoleCode) {
|
||||
this.assigneeRoleCode = assigneeRoleCode;
|
||||
}
|
||||
|
||||
public String getAssigneeType() {
|
||||
return assigneeType;
|
||||
}
|
||||
|
||||
public void setAssigneeType(String assigneeType) {
|
||||
this.assigneeType = assigneeType;
|
||||
}
|
||||
|
||||
public BigInteger getAssigneeTargetId() {
|
||||
return assigneeTargetId;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetId(BigInteger assigneeTargetId) {
|
||||
this.assigneeTargetId = assigneeTargetId;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetCode() {
|
||||
return assigneeTargetCode;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetCode(String assigneeTargetCode) {
|
||||
this.assigneeTargetCode = assigneeTargetCode;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetName() {
|
||||
return assigneeTargetName;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetName(String assigneeTargetName) {
|
||||
this.assigneeTargetName = assigneeTargetName;
|
||||
}
|
||||
|
||||
public BigInteger getActedBy() {
|
||||
return actedBy;
|
||||
}
|
||||
|
||||
public void setActedBy(BigInteger actedBy) {
|
||||
this.actedBy = actedBy;
|
||||
}
|
||||
|
||||
public Date getActedAt() {
|
||||
return actedAt;
|
||||
}
|
||||
|
||||
public void setActedAt(Date actedAt) {
|
||||
this.actedAt = actedAt;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public BigInteger getCreatedBy() {
|
||||
return createdBy;
|
||||
}
|
||||
|
||||
public void setCreatedBy(BigInteger createdBy) {
|
||||
this.createdBy = createdBy;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public BigInteger getModifiedBy() {
|
||||
return modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedBy(BigInteger modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批动作请求。
|
||||
*/
|
||||
public class ApprovalActionRequest {
|
||||
|
||||
private BigInteger instanceId;
|
||||
|
||||
private String comment;
|
||||
|
||||
public BigInteger getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setInstanceId(BigInteger instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批对象选项。
|
||||
*/
|
||||
public class ApprovalAssigneeOptionVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private String code;
|
||||
|
||||
private String name;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批回调上下文。
|
||||
*/
|
||||
public class ApprovalCallbackContext {
|
||||
|
||||
private ApprovalInstance instance;
|
||||
|
||||
private BigInteger operatorId;
|
||||
|
||||
private String comment;
|
||||
|
||||
public ApprovalInstance getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setInstance(ApprovalInstance instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public BigInteger getOperatorId() {
|
||||
return operatorId;
|
||||
}
|
||||
|
||||
public void setOperatorId(BigInteger operatorId) {
|
||||
this.operatorId = operatorId;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批流程详情。
|
||||
*/
|
||||
public class ApprovalFlowDetailVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String resourceType;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private Integer priority;
|
||||
|
||||
private String status;
|
||||
|
||||
private Integer version;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Date created;
|
||||
|
||||
private Date modified;
|
||||
|
||||
private boolean deletable;
|
||||
|
||||
private long pendingInstanceCount;
|
||||
|
||||
private List<ApprovalFlowScopeVo> scopes = new ArrayList<>();
|
||||
|
||||
private List<ApprovalFlowStepVo> steps = new ArrayList<>();
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public Integer getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(Integer priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
|
||||
public boolean isDeletable() {
|
||||
return deletable;
|
||||
}
|
||||
|
||||
public void setDeletable(boolean deletable) {
|
||||
this.deletable = deletable;
|
||||
}
|
||||
|
||||
public long getPendingInstanceCount() {
|
||||
return pendingInstanceCount;
|
||||
}
|
||||
|
||||
public void setPendingInstanceCount(long pendingInstanceCount) {
|
||||
this.pendingInstanceCount = pendingInstanceCount;
|
||||
}
|
||||
|
||||
public List<ApprovalFlowScopeVo> getScopes() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
public void setScopes(List<ApprovalFlowScopeVo> scopes) {
|
||||
this.scopes = scopes;
|
||||
}
|
||||
|
||||
public List<ApprovalFlowStepVo> getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
public void setSteps(List<ApprovalFlowStepVo> steps) {
|
||||
this.steps = steps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 审批流程分页项。
|
||||
*/
|
||||
public class ApprovalFlowPageVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String resourceType;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private Integer priority;
|
||||
|
||||
private String status;
|
||||
|
||||
private Integer version;
|
||||
|
||||
private String scopeSummary;
|
||||
|
||||
private Integer stepCount;
|
||||
|
||||
private long pendingInstanceCount;
|
||||
|
||||
private Date modified;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public Integer getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(Integer priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getScopeSummary() {
|
||||
return scopeSummary;
|
||||
}
|
||||
|
||||
public void setScopeSummary(String scopeSummary) {
|
||||
this.scopeSummary = scopeSummary;
|
||||
}
|
||||
|
||||
public Integer getStepCount() {
|
||||
return stepCount;
|
||||
}
|
||||
|
||||
public void setStepCount(Integer stepCount) {
|
||||
this.stepCount = stepCount;
|
||||
}
|
||||
|
||||
public long getPendingInstanceCount() {
|
||||
return pendingInstanceCount;
|
||||
}
|
||||
|
||||
public void setPendingInstanceCount(long pendingInstanceCount) {
|
||||
this.pendingInstanceCount = pendingInstanceCount;
|
||||
}
|
||||
|
||||
public Date getModified() {
|
||||
return modified;
|
||||
}
|
||||
|
||||
public void setModified(Date modified) {
|
||||
this.modified = modified;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批流程范围项。
|
||||
*/
|
||||
public class ApprovalFlowScopeVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private String scopeType;
|
||||
|
||||
private BigInteger scopeValue;
|
||||
|
||||
private Integer includeChildren;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getScopeType() {
|
||||
return scopeType;
|
||||
}
|
||||
|
||||
public void setScopeType(String scopeType) {
|
||||
this.scopeType = scopeType;
|
||||
}
|
||||
|
||||
public BigInteger getScopeValue() {
|
||||
return scopeValue;
|
||||
}
|
||||
|
||||
public void setScopeValue(BigInteger scopeValue) {
|
||||
this.scopeValue = scopeValue;
|
||||
}
|
||||
|
||||
public Integer getIncludeChildren() {
|
||||
return includeChildren;
|
||||
}
|
||||
|
||||
public void setIncludeChildren(Integer includeChildren) {
|
||||
this.includeChildren = includeChildren;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批流程步骤项。
|
||||
*/
|
||||
public class ApprovalFlowStepVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private Integer stepNo;
|
||||
|
||||
private String stepName;
|
||||
|
||||
private String assigneeType;
|
||||
|
||||
private BigInteger assigneeTargetId;
|
||||
|
||||
private String assigneeTargetCode;
|
||||
|
||||
private String assigneeTargetName;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getStepNo() {
|
||||
return stepNo;
|
||||
}
|
||||
|
||||
public void setStepNo(Integer stepNo) {
|
||||
this.stepNo = stepNo;
|
||||
}
|
||||
|
||||
public String getStepName() {
|
||||
return stepName;
|
||||
}
|
||||
|
||||
public void setStepName(String stepName) {
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
public String getAssigneeType() {
|
||||
return assigneeType;
|
||||
}
|
||||
|
||||
public void setAssigneeType(String assigneeType) {
|
||||
this.assigneeType = assigneeType;
|
||||
}
|
||||
|
||||
public BigInteger getAssigneeTargetId() {
|
||||
return assigneeTargetId;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetId(BigInteger assigneeTargetId) {
|
||||
this.assigneeTargetId = assigneeTargetId;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetCode() {
|
||||
return assigneeTargetCode;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetCode(String assigneeTargetCode) {
|
||||
this.assigneeTargetCode = assigneeTargetCode;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetName() {
|
||||
return assigneeTargetName;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetName(String assigneeTargetName) {
|
||||
this.assigneeTargetName = assigneeTargetName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 审批实例详情。
|
||||
*/
|
||||
public class ApprovalInstanceDetailVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private BigInteger flowId;
|
||||
|
||||
private Integer flowVersion;
|
||||
|
||||
private String resourceType;
|
||||
|
||||
private BigInteger resourceId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private String status;
|
||||
|
||||
private Integer currentStepNo;
|
||||
|
||||
private String summary;
|
||||
|
||||
private BigInteger applicantId;
|
||||
|
||||
private String applicantName;
|
||||
|
||||
private Date submittedAt;
|
||||
|
||||
private Date finishedAt;
|
||||
|
||||
private Map<String, Object> snapshotJson;
|
||||
|
||||
private boolean canApprove;
|
||||
|
||||
private boolean canReject;
|
||||
|
||||
private boolean canRevoke;
|
||||
|
||||
private List<ApprovalTaskVo> tasks = new ArrayList<>();
|
||||
|
||||
private List<ApprovalLogVo> logs = new ArrayList<>();
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public BigInteger getFlowId() {
|
||||
return flowId;
|
||||
}
|
||||
|
||||
public void setFlowId(BigInteger flowId) {
|
||||
this.flowId = flowId;
|
||||
}
|
||||
|
||||
public Integer getFlowVersion() {
|
||||
return flowVersion;
|
||||
}
|
||||
|
||||
public void setFlowVersion(Integer flowVersion) {
|
||||
this.flowVersion = flowVersion;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public BigInteger getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(BigInteger resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getCurrentStepNo() {
|
||||
return currentStepNo;
|
||||
}
|
||||
|
||||
public void setCurrentStepNo(Integer currentStepNo) {
|
||||
this.currentStepNo = currentStepNo;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public BigInteger getApplicantId() {
|
||||
return applicantId;
|
||||
}
|
||||
|
||||
public void setApplicantId(BigInteger applicantId) {
|
||||
this.applicantId = applicantId;
|
||||
}
|
||||
|
||||
public String getApplicantName() {
|
||||
return applicantName;
|
||||
}
|
||||
|
||||
public void setApplicantName(String applicantName) {
|
||||
this.applicantName = applicantName;
|
||||
}
|
||||
|
||||
public Date getSubmittedAt() {
|
||||
return submittedAt;
|
||||
}
|
||||
|
||||
public void setSubmittedAt(Date submittedAt) {
|
||||
this.submittedAt = submittedAt;
|
||||
}
|
||||
|
||||
public Date getFinishedAt() {
|
||||
return finishedAt;
|
||||
}
|
||||
|
||||
public void setFinishedAt(Date finishedAt) {
|
||||
this.finishedAt = finishedAt;
|
||||
}
|
||||
|
||||
public Map<String, Object> getSnapshotJson() {
|
||||
return snapshotJson;
|
||||
}
|
||||
|
||||
public void setSnapshotJson(Map<String, Object> snapshotJson) {
|
||||
this.snapshotJson = snapshotJson;
|
||||
}
|
||||
|
||||
public boolean isCanApprove() {
|
||||
return canApprove;
|
||||
}
|
||||
|
||||
public void setCanApprove(boolean canApprove) {
|
||||
this.canApprove = canApprove;
|
||||
}
|
||||
|
||||
public boolean isCanReject() {
|
||||
return canReject;
|
||||
}
|
||||
|
||||
public void setCanReject(boolean canReject) {
|
||||
this.canReject = canReject;
|
||||
}
|
||||
|
||||
public boolean isCanRevoke() {
|
||||
return canRevoke;
|
||||
}
|
||||
|
||||
public void setCanRevoke(boolean canRevoke) {
|
||||
this.canRevoke = canRevoke;
|
||||
}
|
||||
|
||||
public List<ApprovalTaskVo> getTasks() {
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public void setTasks(List<ApprovalTaskVo> tasks) {
|
||||
this.tasks = tasks;
|
||||
}
|
||||
|
||||
public List<ApprovalLogVo> getLogs() {
|
||||
return logs;
|
||||
}
|
||||
|
||||
public void setLogs(List<ApprovalLogVo> logs) {
|
||||
this.logs = logs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 审批实例分页项。
|
||||
*/
|
||||
public class ApprovalInstancePageVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private String resourceType;
|
||||
|
||||
private BigInteger resourceId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private String status;
|
||||
|
||||
private Integer currentStepNo;
|
||||
|
||||
private String currentStepName;
|
||||
|
||||
private String summary;
|
||||
|
||||
private BigInteger applicantId;
|
||||
|
||||
private Date submittedAt;
|
||||
|
||||
private Date finishedAt;
|
||||
|
||||
private boolean canApprove;
|
||||
|
||||
private boolean canReject;
|
||||
|
||||
private boolean canRevoke;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public BigInteger getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(BigInteger resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Integer getCurrentStepNo() {
|
||||
return currentStepNo;
|
||||
}
|
||||
|
||||
public void setCurrentStepNo(Integer currentStepNo) {
|
||||
this.currentStepNo = currentStepNo;
|
||||
}
|
||||
|
||||
public String getCurrentStepName() {
|
||||
return currentStepName;
|
||||
}
|
||||
|
||||
public void setCurrentStepName(String currentStepName) {
|
||||
this.currentStepName = currentStepName;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public BigInteger getApplicantId() {
|
||||
return applicantId;
|
||||
}
|
||||
|
||||
public void setApplicantId(BigInteger applicantId) {
|
||||
this.applicantId = applicantId;
|
||||
}
|
||||
|
||||
public Date getSubmittedAt() {
|
||||
return submittedAt;
|
||||
}
|
||||
|
||||
public void setSubmittedAt(Date submittedAt) {
|
||||
this.submittedAt = submittedAt;
|
||||
}
|
||||
|
||||
public Date getFinishedAt() {
|
||||
return finishedAt;
|
||||
}
|
||||
|
||||
public void setFinishedAt(Date finishedAt) {
|
||||
this.finishedAt = finishedAt;
|
||||
}
|
||||
|
||||
public boolean isCanApprove() {
|
||||
return canApprove;
|
||||
}
|
||||
|
||||
public void setCanApprove(boolean canApprove) {
|
||||
this.canApprove = canApprove;
|
||||
}
|
||||
|
||||
public boolean isCanReject() {
|
||||
return canReject;
|
||||
}
|
||||
|
||||
public void setCanReject(boolean canReject) {
|
||||
this.canReject = canReject;
|
||||
}
|
||||
|
||||
public boolean isCanRevoke() {
|
||||
return canRevoke;
|
||||
}
|
||||
|
||||
public void setCanRevoke(boolean canRevoke) {
|
||||
this.canRevoke = canRevoke;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 审批日志视图。
|
||||
*/
|
||||
public class ApprovalLogVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private String eventType;
|
||||
|
||||
private BigInteger operatorId;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
private Date created;
|
||||
|
||||
private Map<String, Object> payloadJson;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
public void setEventType(String eventType) {
|
||||
this.eventType = eventType;
|
||||
}
|
||||
|
||||
public BigInteger getOperatorId() {
|
||||
return operatorId;
|
||||
}
|
||||
|
||||
public void setOperatorId(BigInteger operatorId) {
|
||||
this.operatorId = operatorId;
|
||||
}
|
||||
|
||||
public String getOperatorName() {
|
||||
return operatorName;
|
||||
}
|
||||
|
||||
public void setOperatorName(String operatorName) {
|
||||
this.operatorName = operatorName;
|
||||
}
|
||||
|
||||
public Date getCreated() {
|
||||
return created;
|
||||
}
|
||||
|
||||
public void setCreated(Date created) {
|
||||
this.created = created;
|
||||
}
|
||||
|
||||
public Map<String, Object> getPayloadJson() {
|
||||
return payloadJson;
|
||||
}
|
||||
|
||||
public void setPayloadJson(Map<String, Object> payloadJson) {
|
||||
this.payloadJson = payloadJson;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批提交后回调上下文。
|
||||
*/
|
||||
public class ApprovalSubmitCallbackContext {
|
||||
|
||||
private BigInteger instanceId;
|
||||
|
||||
private String resourceType;
|
||||
|
||||
private BigInteger resourceId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private BigInteger operatorId;
|
||||
|
||||
public BigInteger getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setInstanceId(BigInteger instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public BigInteger getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(BigInteger resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public BigInteger getOperatorId() {
|
||||
return operatorId;
|
||||
}
|
||||
|
||||
public void setOperatorId(BigInteger operatorId) {
|
||||
this.operatorId = operatorId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 发起审批请求。
|
||||
*/
|
||||
public class ApprovalSubmitRequest {
|
||||
|
||||
private String resourceType;
|
||||
|
||||
private BigInteger resourceId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private BigInteger categoryId;
|
||||
|
||||
private BigInteger deptId;
|
||||
|
||||
private BigInteger applicantId;
|
||||
|
||||
private String summary;
|
||||
|
||||
private Map<String, Object> snapshotJson;
|
||||
|
||||
public String getResourceType() {
|
||||
return resourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String resourceType) {
|
||||
this.resourceType = resourceType;
|
||||
}
|
||||
|
||||
public BigInteger getResourceId() {
|
||||
return resourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(BigInteger resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public BigInteger getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(BigInteger categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
public BigInteger getDeptId() {
|
||||
return deptId;
|
||||
}
|
||||
|
||||
public void setDeptId(BigInteger deptId) {
|
||||
this.deptId = deptId;
|
||||
}
|
||||
|
||||
public BigInteger getApplicantId() {
|
||||
return applicantId;
|
||||
}
|
||||
|
||||
public void setApplicantId(BigInteger applicantId) {
|
||||
this.applicantId = applicantId;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public Map<String, Object> getSnapshotJson() {
|
||||
return snapshotJson;
|
||||
}
|
||||
|
||||
public void setSnapshotJson(Map<String, Object> snapshotJson) {
|
||||
this.snapshotJson = snapshotJson;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package tech.easyflow.approval.entity.vo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 审批任务视图。
|
||||
*/
|
||||
public class ApprovalTaskVo {
|
||||
|
||||
private BigInteger id;
|
||||
|
||||
private Integer stepNo;
|
||||
|
||||
private String stepName;
|
||||
|
||||
private String status;
|
||||
|
||||
private String assigneeRoleCode;
|
||||
|
||||
private String assigneeType;
|
||||
|
||||
private BigInteger assigneeTargetId;
|
||||
|
||||
private String assigneeTargetCode;
|
||||
|
||||
private String assigneeTargetName;
|
||||
|
||||
private BigInteger actedBy;
|
||||
|
||||
private String actedByName;
|
||||
|
||||
private Date actedAt;
|
||||
|
||||
private String comment;
|
||||
|
||||
public BigInteger getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(BigInteger id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getStepNo() {
|
||||
return stepNo;
|
||||
}
|
||||
|
||||
public void setStepNo(Integer stepNo) {
|
||||
this.stepNo = stepNo;
|
||||
}
|
||||
|
||||
public String getStepName() {
|
||||
return stepName;
|
||||
}
|
||||
|
||||
public void setStepName(String stepName) {
|
||||
this.stepName = stepName;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getAssigneeRoleCode() {
|
||||
return assigneeRoleCode;
|
||||
}
|
||||
|
||||
public void setAssigneeRoleCode(String assigneeRoleCode) {
|
||||
this.assigneeRoleCode = assigneeRoleCode;
|
||||
}
|
||||
|
||||
public String getAssigneeType() {
|
||||
return assigneeType;
|
||||
}
|
||||
|
||||
public void setAssigneeType(String assigneeType) {
|
||||
this.assigneeType = assigneeType;
|
||||
}
|
||||
|
||||
public BigInteger getAssigneeTargetId() {
|
||||
return assigneeTargetId;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetId(BigInteger assigneeTargetId) {
|
||||
this.assigneeTargetId = assigneeTargetId;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetCode() {
|
||||
return assigneeTargetCode;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetCode(String assigneeTargetCode) {
|
||||
this.assigneeTargetCode = assigneeTargetCode;
|
||||
}
|
||||
|
||||
public String getAssigneeTargetName() {
|
||||
return assigneeTargetName;
|
||||
}
|
||||
|
||||
public void setAssigneeTargetName(String assigneeTargetName) {
|
||||
this.assigneeTargetName = assigneeTargetName;
|
||||
}
|
||||
|
||||
public BigInteger getActedBy() {
|
||||
return actedBy;
|
||||
}
|
||||
|
||||
public void setActedBy(BigInteger actedBy) {
|
||||
this.actedBy = actedBy;
|
||||
}
|
||||
|
||||
public String getActedByName() {
|
||||
return actedByName;
|
||||
}
|
||||
|
||||
public void setActedByName(String actedByName) {
|
||||
this.actedByName = actedByName;
|
||||
}
|
||||
|
||||
public Date getActedAt() {
|
||||
return actedAt;
|
||||
}
|
||||
|
||||
public void setActedAt(Date actedAt) {
|
||||
this.actedAt = actedAt;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 审批动作类型枚举。
|
||||
*/
|
||||
public enum ApprovalActionType {
|
||||
|
||||
PUBLISH("PUBLISH"),
|
||||
DELETE("DELETE");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalActionType(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动作类型编码。
|
||||
*
|
||||
* @return 动作类型编码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析动作类型。
|
||||
*
|
||||
* @param code 动作类型编码
|
||||
* @return 动作类型
|
||||
*/
|
||||
public static ApprovalActionType from(String code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("actionType不能为空");
|
||||
}
|
||||
String normalized = code.trim().toUpperCase(Locale.ROOT);
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equals(normalized))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("不支持的actionType: " + code));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
|
||||
/**
|
||||
* 审批对象类型。
|
||||
*/
|
||||
public enum ApprovalAssigneeType {
|
||||
|
||||
ROLE("ROLE"),
|
||||
USER("USER");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalAssigneeType(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析审批对象类型。
|
||||
*
|
||||
* @param code 类型编码
|
||||
* @return 审批对象类型
|
||||
*/
|
||||
public static ApprovalAssigneeType from(String code) {
|
||||
if (!StringUtils.hasText(code)) {
|
||||
throw new BusinessException("审批对象类型不能为空");
|
||||
}
|
||||
for (ApprovalAssigneeType value : values()) {
|
||||
if (value.code.equalsIgnoreCase(code.trim())) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new BusinessException("审批对象类型非法");
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 审批实例日志事件枚举。
|
||||
*/
|
||||
public enum ApprovalEventType {
|
||||
|
||||
SUBMITTED("SUBMITTED"),
|
||||
STEP_CREATED("STEP_CREATED"),
|
||||
APPROVED("APPROVED"),
|
||||
REJECTED("REJECTED"),
|
||||
REVOKED("REVOKED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalEventType(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件编码。
|
||||
*
|
||||
* @return 事件编码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析事件类型。
|
||||
*
|
||||
* @param code 事件编码
|
||||
* @return 事件类型
|
||||
*/
|
||||
public static ApprovalEventType from(String code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("eventType不能为空");
|
||||
}
|
||||
String normalized = code.trim().toUpperCase(Locale.ROOT);
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equals(normalized))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("不支持的eventType: " + code));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 审批流程状态枚举。
|
||||
*/
|
||||
public enum ApprovalFlowStatus {
|
||||
|
||||
ENABLED("ENABLED"),
|
||||
DISABLED("DISABLED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalFlowStatus(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态编码。
|
||||
*
|
||||
* @return 状态编码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析流程状态。
|
||||
*
|
||||
* @param code 状态编码
|
||||
* @return 流程状态
|
||||
*/
|
||||
public static ApprovalFlowStatus from(String code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("status不能为空");
|
||||
}
|
||||
String normalized = code.trim().toUpperCase(Locale.ROOT);
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equals(normalized))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("不支持的status: " + code));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 审批实例状态枚举。
|
||||
*/
|
||||
public enum ApprovalInstanceStatus {
|
||||
|
||||
PENDING("PENDING"),
|
||||
PROCESSING("PROCESSING"),
|
||||
APPROVED("APPROVED"),
|
||||
REJECTED("REJECTED"),
|
||||
REVOKED("REVOKED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalInstanceStatus(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态编码。
|
||||
*
|
||||
* @return 状态编码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否已结束。
|
||||
*
|
||||
* @return 是否结束
|
||||
*/
|
||||
public boolean isFinished() {
|
||||
return this == APPROVED || this == REJECTED || this == REVOKED;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析实例状态。
|
||||
*
|
||||
* @param code 状态编码
|
||||
* @return 实例状态
|
||||
*/
|
||||
public static ApprovalInstanceStatus from(String code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("status不能为空");
|
||||
}
|
||||
String normalized = code.trim().toUpperCase(Locale.ROOT);
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equals(normalized))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("不支持的status: " + code));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 审批资源类型枚举。
|
||||
*/
|
||||
public enum ApprovalResourceType {
|
||||
|
||||
BOT("BOT"),
|
||||
WORKFLOW("WORKFLOW"),
|
||||
KNOWLEDGE("KNOWLEDGE");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalResourceType(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源类型编码。
|
||||
*
|
||||
* @return 资源类型编码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析资源类型。
|
||||
*
|
||||
* @param code 资源类型编码
|
||||
* @return 资源类型
|
||||
*/
|
||||
public static ApprovalResourceType from(String code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("resourceType不能为空");
|
||||
}
|
||||
String normalized = code.trim().toUpperCase(Locale.ROOT);
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equals(normalized))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("不支持的resourceType: " + code));
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回全部资源类型编码。
|
||||
*
|
||||
* @return 资源类型编码列表
|
||||
*/
|
||||
public static List<String> allCodes() {
|
||||
return Arrays.stream(values()).map(ApprovalResourceType::getCode).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 审批流程范围类型枚举。
|
||||
*/
|
||||
public enum ApprovalScopeType {
|
||||
|
||||
CATEGORY("CATEGORY"),
|
||||
DEPT("DEPT");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalScopeType(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取范围类型编码。
|
||||
*
|
||||
* @return 范围类型编码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析范围类型。
|
||||
*
|
||||
* @param code 范围类型编码
|
||||
* @return 范围类型
|
||||
*/
|
||||
public static ApprovalScopeType from(String code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("scopeType不能为空");
|
||||
}
|
||||
String normalized = code.trim().toUpperCase(Locale.ROOT);
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equals(normalized))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("不支持的scopeType: " + code));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package tech.easyflow.approval.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 审批任务状态枚举。
|
||||
*/
|
||||
public enum ApprovalTaskStatus {
|
||||
|
||||
PENDING("PENDING"),
|
||||
APPROVED("APPROVED"),
|
||||
REJECTED("REJECTED"),
|
||||
REVOKED("REVOKED");
|
||||
|
||||
private final String code;
|
||||
|
||||
ApprovalTaskStatus(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取任务状态编码。
|
||||
*
|
||||
* @return 任务状态编码
|
||||
*/
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析任务状态。
|
||||
*
|
||||
* @param code 任务状态编码
|
||||
* @return 任务状态
|
||||
*/
|
||||
public static ApprovalTaskStatus from(String code) {
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("taskStatus不能为空");
|
||||
}
|
||||
String normalized = code.trim().toUpperCase(Locale.ROOT);
|
||||
return Arrays.stream(values())
|
||||
.filter(item -> item.code.equals(normalized))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalArgumentException("不支持的taskStatus: " + code));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package tech.easyflow.approval.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import tech.easyflow.approval.entity.ApprovalFlow;
|
||||
|
||||
/**
|
||||
* 审批流程 Mapper。
|
||||
*/
|
||||
public interface ApprovalFlowMapper extends BaseMapper<ApprovalFlow> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package tech.easyflow.approval.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import tech.easyflow.approval.entity.ApprovalFlowScope;
|
||||
|
||||
/**
|
||||
* 审批流程范围 Mapper。
|
||||
*/
|
||||
public interface ApprovalFlowScopeMapper extends BaseMapper<ApprovalFlowScope> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package tech.easyflow.approval.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import tech.easyflow.approval.entity.ApprovalFlowStep;
|
||||
|
||||
/**
|
||||
* 审批流程步骤 Mapper。
|
||||
*/
|
||||
public interface ApprovalFlowStepMapper extends BaseMapper<ApprovalFlowStep> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package tech.easyflow.approval.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
|
||||
/**
|
||||
* 审批实例 Mapper。
|
||||
*/
|
||||
public interface ApprovalInstanceMapper extends BaseMapper<ApprovalInstance> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package tech.easyflow.approval.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import tech.easyflow.approval.entity.ApprovalLog;
|
||||
|
||||
/**
|
||||
* 审批日志 Mapper。
|
||||
*/
|
||||
public interface ApprovalLogMapper extends BaseMapper<ApprovalLog> {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package tech.easyflow.approval.mapper;
|
||||
|
||||
import com.mybatisflex.core.BaseMapper;
|
||||
import tech.easyflow.approval.entity.ApprovalTask;
|
||||
|
||||
/**
|
||||
* 审批任务 Mapper。
|
||||
*/
|
||||
public interface ApprovalTaskMapper extends BaseMapper<ApprovalTask> {
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package tech.easyflow.approval.service;
|
||||
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批动作门面。
|
||||
*/
|
||||
public interface ApprovalActionFacade {
|
||||
|
||||
/**
|
||||
* 提交审批。
|
||||
*
|
||||
* @param resourceType 资源类型
|
||||
* @param resourceId 资源 ID
|
||||
* @param actionType 动作类型
|
||||
* @param operatorId 操作人 ID
|
||||
* @return 审批实例 ID
|
||||
*/
|
||||
BigInteger submit(String resourceType, BigInteger resourceId, String actionType, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 处理审批通过后的业务回调。
|
||||
*
|
||||
* @param instance 审批实例
|
||||
* @param operatorId 操作人 ID
|
||||
* @param comment 审批意见
|
||||
*/
|
||||
void handleApproved(ApprovalInstance instance, BigInteger operatorId, String comment);
|
||||
|
||||
/**
|
||||
* 处理驳回后的业务回调。
|
||||
*
|
||||
* @param instance 审批实例
|
||||
* @param operatorId 操作人 ID
|
||||
* @param comment 审批意见
|
||||
*/
|
||||
void handleRejected(ApprovalInstance instance, BigInteger operatorId, String comment);
|
||||
|
||||
/**
|
||||
* 处理撤回后的业务回调。
|
||||
*
|
||||
* @param instance 审批实例
|
||||
* @param operatorId 操作人 ID
|
||||
* @param comment 审批意见
|
||||
*/
|
||||
void handleRevoked(ApprovalInstance instance, BigInteger operatorId, String comment);
|
||||
|
||||
/**
|
||||
* 校验已发布访问权限。
|
||||
*
|
||||
* @param resourceType 资源类型
|
||||
* @param identifier 资源标识
|
||||
* @param denyMessage 拒绝提示
|
||||
*/
|
||||
void assertPublishedAccess(String resourceType, Object identifier, String denyMessage);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package tech.easyflow.approval.service;
|
||||
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import tech.easyflow.approval.entity.ApprovalTask;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalAssigneeOptionVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 审批对象服务。
|
||||
*/
|
||||
public interface ApprovalAssigneeService {
|
||||
|
||||
/**
|
||||
* 规范化并校验审批步骤中的审批对象配置。
|
||||
*
|
||||
* @param step 步骤配置
|
||||
* @return 规范化后的步骤配置
|
||||
*/
|
||||
ApprovalFlowStepVo normalizeStepAssignee(ApprovalFlowStepVo step);
|
||||
|
||||
/**
|
||||
* 查询可用角色选项。
|
||||
*
|
||||
* @return 角色选项
|
||||
*/
|
||||
List<ApprovalAssigneeOptionVo> listRoleOptions();
|
||||
|
||||
/**
|
||||
* 分页查询可用用户选项。
|
||||
*
|
||||
* @param keyword 关键词
|
||||
* @param pageNumber 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return 用户选项分页
|
||||
*/
|
||||
Page<ApprovalAssigneeOptionVo> pageAccountOptions(String keyword, Long pageNumber, Long pageSize);
|
||||
|
||||
/**
|
||||
* 查询指定账号当前仍有效的角色 ID 集合。
|
||||
*
|
||||
* @param accountId 账号 ID
|
||||
* @return 角色 ID 集合
|
||||
*/
|
||||
Set<BigInteger> getAvailableRoleIds(BigInteger accountId);
|
||||
|
||||
/**
|
||||
* 判断当前账号是否命中审批任务。
|
||||
*
|
||||
* @param task 审批任务
|
||||
* @param operatorId 当前操作人
|
||||
* @param roleIds 当前操作人的有效角色集合
|
||||
* @return 是否命中
|
||||
*/
|
||||
boolean canHandleTask(ApprovalTask task, BigInteger operatorId, Set<BigInteger> roleIds);
|
||||
|
||||
/**
|
||||
* 查询当前账号命中的待审批实例 ID 集合。
|
||||
*
|
||||
* @param operatorId 当前操作人
|
||||
* @param roleIds 当前操作人的有效角色集合
|
||||
* @param instanceIds 可选的实例 ID 过滤条件
|
||||
* @return 命中的实例 ID 集合
|
||||
*/
|
||||
Set<BigInteger> listPendingInstanceIds(BigInteger operatorId, Set<BigInteger> roleIds, List<BigInteger> instanceIds);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package tech.easyflow.approval.service;
|
||||
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.service.IService;
|
||||
import tech.easyflow.approval.entity.ApprovalFlow;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowDetailVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowPageVo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批流程服务。
|
||||
*/
|
||||
public interface ApprovalFlowService extends IService<ApprovalFlow> {
|
||||
|
||||
/**
|
||||
* 分页查询审批流程。
|
||||
*
|
||||
* @param name 流程名称
|
||||
* @param resourceType 资源类型
|
||||
* @param actionType 动作类型
|
||||
* @param status 流程状态
|
||||
* @param pageNumber 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return 流程分页结果
|
||||
*/
|
||||
Page<ApprovalFlowPageVo> pageFlows(String name, String resourceType, String actionType, String status,
|
||||
Long pageNumber, Long pageSize);
|
||||
|
||||
/**
|
||||
* 查询审批流程详情。
|
||||
*
|
||||
* @param id 流程ID
|
||||
* @return 流程详情
|
||||
*/
|
||||
ApprovalFlowDetailVo getFlowDetail(BigInteger id);
|
||||
|
||||
/**
|
||||
* 新增审批流程。
|
||||
*
|
||||
* @param request 流程详情请求
|
||||
* @param operatorId 操作人ID
|
||||
* @return 新增后的流程ID
|
||||
*/
|
||||
BigInteger saveFlow(ApprovalFlowDetailVo request, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 更新审批流程。
|
||||
*
|
||||
* @param request 流程详情请求
|
||||
* @param operatorId 操作人ID
|
||||
*/
|
||||
void updateFlow(ApprovalFlowDetailVo request, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 启用审批流程。
|
||||
*
|
||||
* @param id 流程ID
|
||||
* @param operatorId 操作人ID
|
||||
*/
|
||||
void enableFlow(BigInteger id, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 停用审批流程。
|
||||
*
|
||||
* @param id 流程ID
|
||||
* @param operatorId 操作人ID
|
||||
*/
|
||||
void disableFlow(BigInteger id, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 删除审批流程。
|
||||
*
|
||||
* @param id 流程ID
|
||||
*/
|
||||
void removeFlow(BigInteger id);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package tech.easyflow.approval.service;
|
||||
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批实例服务。
|
||||
*/
|
||||
public interface ApprovalInstanceService {
|
||||
|
||||
/**
|
||||
* 发起审批。
|
||||
*
|
||||
* @param request 发起审批请求
|
||||
* @return 审批实例ID
|
||||
*/
|
||||
BigInteger submitApproval(ApprovalSubmitRequest request);
|
||||
|
||||
/**
|
||||
* 通过审批。
|
||||
*
|
||||
* @param instanceId 审批实例ID
|
||||
* @param comment 审批意见
|
||||
* @param operatorId 操作人ID
|
||||
*/
|
||||
void approve(BigInteger instanceId, String comment, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 驳回审批。
|
||||
*
|
||||
* @param instanceId 审批实例ID
|
||||
* @param comment 审批意见
|
||||
* @param operatorId 操作人ID
|
||||
*/
|
||||
void reject(BigInteger instanceId, String comment, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 撤回审批。
|
||||
*
|
||||
* @param instanceId 审批实例ID
|
||||
* @param comment 撤回说明
|
||||
* @param operatorId 操作人ID
|
||||
*/
|
||||
void revoke(BigInteger instanceId, String comment, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 判断资源是否存在未结束审批实例。
|
||||
*
|
||||
* @param resourceType 资源类型
|
||||
* @param resourceId 资源 ID
|
||||
* @return 存在未结束实例时返回 true
|
||||
*/
|
||||
boolean existsActiveInstance(String resourceType, BigInteger resourceId);
|
||||
|
||||
/**
|
||||
* 根据实例 ID 查询实例。
|
||||
*
|
||||
* @param instanceId 审批实例 ID
|
||||
* @return 审批实例
|
||||
*/
|
||||
ApprovalInstance getById(BigInteger instanceId);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package tech.easyflow.approval.service;
|
||||
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowDetailVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
|
||||
|
||||
/**
|
||||
* 审批流程匹配服务。
|
||||
*/
|
||||
public interface ApprovalMatchService {
|
||||
|
||||
/**
|
||||
* 根据资源上下文匹配审批流程。
|
||||
*
|
||||
* @param request 审批提交请求
|
||||
* @return 命中的流程详情
|
||||
*/
|
||||
ApprovalFlowDetailVo matchFlow(ApprovalSubmitRequest request);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package tech.easyflow.approval.service;
|
||||
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalInstanceDetailVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalInstancePageVo;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批查询服务。
|
||||
*/
|
||||
public interface ApprovalQueryService {
|
||||
|
||||
/**
|
||||
* 分页查询待审批列表。
|
||||
*
|
||||
* @param resourceType 资源类型
|
||||
* @param actionType 动作类型
|
||||
* @param keyword 关键词
|
||||
* @param pageNumber 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return 待审批分页
|
||||
*/
|
||||
Page<ApprovalInstancePageVo> pendingPage(String resourceType, String actionType, String keyword,
|
||||
Long pageNumber, Long pageSize);
|
||||
|
||||
/**
|
||||
* 分页查询已审批列表。
|
||||
*
|
||||
* @param resourceType 资源类型
|
||||
* @param actionType 动作类型
|
||||
* @param keyword 关键词
|
||||
* @param pageNumber 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return 已审批分页
|
||||
*/
|
||||
Page<ApprovalInstancePageVo> processedPage(String resourceType, String actionType, String keyword,
|
||||
Long pageNumber, Long pageSize);
|
||||
|
||||
/**
|
||||
* 分页查询我发起的审批列表。
|
||||
*
|
||||
* @param resourceType 资源类型
|
||||
* @param actionType 动作类型
|
||||
* @param keyword 关键词
|
||||
* @param pageNumber 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return 我发起的审批分页
|
||||
*/
|
||||
Page<ApprovalInstancePageVo> initiatedPage(String resourceType, String actionType, String keyword,
|
||||
Long pageNumber, Long pageSize);
|
||||
|
||||
/**
|
||||
* 查询审批实例详情。
|
||||
*
|
||||
* @param instanceId 审批实例ID
|
||||
* @return 审批实例详情
|
||||
*/
|
||||
ApprovalInstanceDetailVo detail(BigInteger instanceId);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package tech.easyflow.approval.service;
|
||||
|
||||
import tech.easyflow.approval.entity.vo.ApprovalCallbackContext;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitCallbackContext;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* 审批资源处理器。
|
||||
*/
|
||||
public interface ApprovalSubjectHandler {
|
||||
|
||||
/**
|
||||
* 当前处理器支持的资源类型。
|
||||
*
|
||||
* @return 资源类型编码
|
||||
*/
|
||||
String resourceType();
|
||||
|
||||
/**
|
||||
* 构建审批提交请求。
|
||||
*
|
||||
* @param resourceId 资源 ID
|
||||
* @param actionType 动作类型
|
||||
* @param operatorId 操作人 ID
|
||||
* @return 审批请求
|
||||
*/
|
||||
ApprovalSubmitRequest buildSubmitRequest(BigInteger resourceId, String actionType, BigInteger operatorId);
|
||||
|
||||
/**
|
||||
* 审批提交完成后的回调。
|
||||
*
|
||||
* @param context 回调上下文
|
||||
*/
|
||||
void onSubmitted(ApprovalSubmitCallbackContext context);
|
||||
|
||||
/**
|
||||
* 审批通过后的回调。
|
||||
*
|
||||
* @param context 回调上下文
|
||||
*/
|
||||
void onApproved(ApprovalCallbackContext context);
|
||||
|
||||
/**
|
||||
* 审批驳回后的回调。
|
||||
*
|
||||
* @param context 回调上下文
|
||||
*/
|
||||
void onRejected(ApprovalCallbackContext context);
|
||||
|
||||
/**
|
||||
* 审批撤回后的回调。
|
||||
*
|
||||
* @param context 回调上下文
|
||||
*/
|
||||
void onRevoked(ApprovalCallbackContext context);
|
||||
|
||||
/**
|
||||
* 校验资源是否已发布。
|
||||
*
|
||||
* @param identifier 资源标识(ID 或别名)
|
||||
* @param denyMessage 拒绝提示
|
||||
*/
|
||||
void assertPublishedAccess(Object identifier, String denyMessage);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package tech.easyflow.approval.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalCallbackContext;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitCallbackContext;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
|
||||
import tech.easyflow.approval.enums.ApprovalResourceType;
|
||||
import tech.easyflow.approval.service.ApprovalActionFacade;
|
||||
import tech.easyflow.approval.service.ApprovalInstanceService;
|
||||
import tech.easyflow.approval.service.ApprovalSubjectHandler;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审批动作门面实现。
|
||||
*/
|
||||
@Service
|
||||
public class ApprovalActionFacadeImpl implements ApprovalActionFacade {
|
||||
|
||||
private final List<ApprovalSubjectHandler> handlers;
|
||||
private final ApprovalInstanceService approvalInstanceService;
|
||||
|
||||
public ApprovalActionFacadeImpl(List<ApprovalSubjectHandler> handlers,
|
||||
ApprovalInstanceService approvalInstanceService) {
|
||||
this.handlers = handlers;
|
||||
this.approvalInstanceService = approvalInstanceService;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public BigInteger submit(String resourceType, BigInteger resourceId, String actionType, BigInteger operatorId) {
|
||||
ApprovalSubjectHandler handler = getHandler(resourceType);
|
||||
ApprovalSubmitRequest request = handler.buildSubmitRequest(resourceId, actionType, operatorId);
|
||||
BigInteger instanceId = approvalInstanceService.submitApproval(request);
|
||||
|
||||
ApprovalSubmitCallbackContext context = new ApprovalSubmitCallbackContext();
|
||||
context.setInstanceId(instanceId);
|
||||
context.setResourceType(request.getResourceType());
|
||||
context.setResourceId(request.getResourceId());
|
||||
context.setActionType(request.getActionType());
|
||||
context.setOperatorId(operatorId);
|
||||
handler.onSubmitted(context);
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void handleApproved(ApprovalInstance instance, BigInteger operatorId, String comment) {
|
||||
ApprovalSubjectHandler handler = getHandler(instance.getResourceType());
|
||||
handler.onApproved(buildCallbackContext(instance, operatorId, comment));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void handleRejected(ApprovalInstance instance, BigInteger operatorId, String comment) {
|
||||
ApprovalSubjectHandler handler = getHandler(instance.getResourceType());
|
||||
handler.onRejected(buildCallbackContext(instance, operatorId, comment));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void handleRevoked(ApprovalInstance instance, BigInteger operatorId, String comment) {
|
||||
ApprovalSubjectHandler handler = getHandler(instance.getResourceType());
|
||||
handler.onRevoked(buildCallbackContext(instance, operatorId, comment));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void assertPublishedAccess(String resourceType, Object identifier, String denyMessage) {
|
||||
ApprovalSubjectHandler handler = getHandler(resourceType);
|
||||
handler.assertPublishedAccess(identifier, denyMessage);
|
||||
}
|
||||
|
||||
private ApprovalCallbackContext buildCallbackContext(ApprovalInstance instance, BigInteger operatorId, String comment) {
|
||||
ApprovalCallbackContext context = new ApprovalCallbackContext();
|
||||
context.setInstance(instance);
|
||||
context.setOperatorId(operatorId);
|
||||
context.setComment(comment);
|
||||
return context;
|
||||
}
|
||||
|
||||
private ApprovalSubjectHandler getHandler(String resourceType) {
|
||||
String normalized = ApprovalResourceType.from(resourceType).getCode();
|
||||
return handlers.stream()
|
||||
.filter(item -> normalized.equals(item.resourceType()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("未找到审批处理器: " + resourceType));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package tech.easyflow.approval.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import tech.easyflow.approval.entity.ApprovalTask;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalAssigneeOptionVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
|
||||
import tech.easyflow.approval.enums.ApprovalAssigneeType;
|
||||
import tech.easyflow.approval.enums.ApprovalTaskStatus;
|
||||
import tech.easyflow.approval.mapper.ApprovalTaskMapper;
|
||||
import tech.easyflow.approval.service.ApprovalAssigneeService;
|
||||
import tech.easyflow.common.constant.enums.EnumDataStatus;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.system.entity.SysAccount;
|
||||
import tech.easyflow.system.entity.SysRole;
|
||||
import tech.easyflow.system.service.SysAccountService;
|
||||
import tech.easyflow.system.service.SysRoleService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 审批对象服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ApprovalAssigneeServiceImpl implements ApprovalAssigneeService {
|
||||
|
||||
@Resource
|
||||
private SysRoleService sysRoleService;
|
||||
|
||||
@Resource
|
||||
private SysAccountService sysAccountService;
|
||||
|
||||
@Resource
|
||||
private ApprovalTaskMapper approvalTaskMapper;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ApprovalFlowStepVo normalizeStepAssignee(ApprovalFlowStepVo step) {
|
||||
if (step == null) {
|
||||
throw new BusinessException("审批步骤不能为空");
|
||||
}
|
||||
ApprovalAssigneeType assigneeType = ApprovalAssigneeType.from(step.getAssigneeType());
|
||||
BigInteger targetId = step.getAssigneeTargetId();
|
||||
if (targetId == null) {
|
||||
throw new BusinessException("审批对象不能为空");
|
||||
}
|
||||
ApprovalAssigneeOptionVo option = resolveAssigneeOption(assigneeType, targetId);
|
||||
step.setAssigneeType(assigneeType.getCode());
|
||||
step.setAssigneeTargetId(option.getId());
|
||||
step.setAssigneeTargetCode(option.getCode());
|
||||
step.setAssigneeTargetName(option.getName());
|
||||
return step;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public List<ApprovalAssigneeOptionVo> listRoleOptions() {
|
||||
return sysRoleService.list(QueryWrapper.create()
|
||||
.eq(SysRole::getStatus, EnumDataStatus.AVAILABLE.getCode())
|
||||
.orderBy("role_name asc, id asc"))
|
||||
.stream()
|
||||
.map(this::toRoleOption)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Page<ApprovalAssigneeOptionVo> pageAccountOptions(String keyword, Long pageNumber, Long pageSize) {
|
||||
long actualPageNumber = pageNumber == null || pageNumber < 1 ? 1L : pageNumber;
|
||||
long actualPageSize = pageSize == null || pageSize < 1 ? 20L : pageSize;
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(SysAccount::getStatus, EnumDataStatus.AVAILABLE.getCode())
|
||||
.orderBy("nickname asc, login_name asc, id asc");
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
String likeKeyword = "%" + keyword.trim() + "%";
|
||||
queryWrapper.and("(`login_name` like ? or `nickname` like ?)", likeKeyword, likeKeyword);
|
||||
}
|
||||
Page<SysAccount> page = sysAccountService.page(new Page<>(actualPageNumber, actualPageSize), queryWrapper);
|
||||
Page<ApprovalAssigneeOptionVo> result = new Page<>(actualPageNumber, actualPageSize, page.getTotalRow());
|
||||
result.setRecords(page.getRecords().stream().map(this::toAccountOption).collect(Collectors.toList()));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Set<BigInteger> getAvailableRoleIds(BigInteger accountId) {
|
||||
if (accountId == null) {
|
||||
return Set.of();
|
||||
}
|
||||
return sysRoleService.getRolesByAccountId(accountId).stream()
|
||||
.filter(role -> role != null && EnumDataStatus.AVAILABLE.getCode().equals(role.getStatus()))
|
||||
.map(SysRole::getId)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean canHandleTask(ApprovalTask task, BigInteger operatorId, Set<BigInteger> roleIds) {
|
||||
if (task == null || operatorId == null) {
|
||||
return false;
|
||||
}
|
||||
ApprovalAssigneeType assigneeType = ApprovalAssigneeType.from(task.getAssigneeType());
|
||||
if (ApprovalAssigneeType.USER == assigneeType) {
|
||||
return operatorId.equals(task.getAssigneeTargetId());
|
||||
}
|
||||
return roleIds != null && roleIds.contains(task.getAssigneeTargetId());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Set<BigInteger> listPendingInstanceIds(BigInteger operatorId, Set<BigInteger> roleIds, List<BigInteger> instanceIds) {
|
||||
if (operatorId == null) {
|
||||
return Set.of();
|
||||
}
|
||||
Set<BigInteger> result = new LinkedHashSet<>();
|
||||
QueryWrapper userQuery = QueryWrapper.create()
|
||||
.eq(ApprovalTask::getStatus, ApprovalTaskStatus.PENDING.getCode());
|
||||
if (CollectionUtil.isNotEmpty(instanceIds)) {
|
||||
userQuery.in(ApprovalTask::getInstanceId, instanceIds);
|
||||
}
|
||||
userQuery.eq(ApprovalTask::getAssigneeType, ApprovalAssigneeType.USER.getCode())
|
||||
.eq(ApprovalTask::getAssigneeTargetId, operatorId);
|
||||
result.addAll(approvalTaskMapper.selectListByQuery(userQuery).stream()
|
||||
.map(ApprovalTask::getInstanceId)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
if (CollectionUtil.isNotEmpty(roleIds)) {
|
||||
QueryWrapper roleQuery = QueryWrapper.create()
|
||||
.eq(ApprovalTask::getStatus, ApprovalTaskStatus.PENDING.getCode())
|
||||
.eq(ApprovalTask::getAssigneeType, ApprovalAssigneeType.ROLE.getCode())
|
||||
.in(ApprovalTask::getAssigneeTargetId, roleIds);
|
||||
if (CollectionUtil.isNotEmpty(instanceIds)) {
|
||||
roleQuery.in(ApprovalTask::getInstanceId, instanceIds);
|
||||
}
|
||||
result.addAll(approvalTaskMapper.selectListByQuery(roleQuery).stream()
|
||||
.map(ApprovalTask::getInstanceId)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private ApprovalAssigneeOptionVo resolveAssigneeOption(ApprovalAssigneeType assigneeType, BigInteger targetId) {
|
||||
if (ApprovalAssigneeType.ROLE == assigneeType) {
|
||||
SysRole role = sysRoleService.getById(targetId);
|
||||
if (role == null || !EnumDataStatus.AVAILABLE.getCode().equals(role.getStatus())) {
|
||||
throw new BusinessException("审批角色不存在或未启用");
|
||||
}
|
||||
return toRoleOption(role);
|
||||
}
|
||||
SysAccount account = sysAccountService.getById(targetId);
|
||||
if (account == null || !EnumDataStatus.AVAILABLE.getCode().equals(account.getStatus())) {
|
||||
throw new BusinessException("审批用户不存在或未启用");
|
||||
}
|
||||
return toAccountOption(account);
|
||||
}
|
||||
|
||||
private ApprovalAssigneeOptionVo toRoleOption(SysRole role) {
|
||||
ApprovalAssigneeOptionVo option = new ApprovalAssigneeOptionVo();
|
||||
option.setId(role.getId());
|
||||
option.setCode(role.getRoleKey());
|
||||
option.setName(role.getRoleName());
|
||||
return option;
|
||||
}
|
||||
|
||||
private ApprovalAssigneeOptionVo toAccountOption(SysAccount account) {
|
||||
ApprovalAssigneeOptionVo option = new ApprovalAssigneeOptionVo();
|
||||
option.setId(account.getId());
|
||||
option.setCode(account.getLoginName());
|
||||
option.setName(resolveAccountDisplayName(account));
|
||||
return option;
|
||||
}
|
||||
|
||||
private String resolveAccountDisplayName(SysAccount account) {
|
||||
if (StringUtils.hasText(account.getNickname())) {
|
||||
return account.getNickname().trim();
|
||||
}
|
||||
if (StringUtils.hasText(account.getLoginName())) {
|
||||
return account.getLoginName().trim();
|
||||
}
|
||||
return String.valueOf(account.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
package tech.easyflow.approval.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import com.mybatisflex.spring.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.approval.entity.ApprovalFlow;
|
||||
import tech.easyflow.approval.entity.ApprovalFlowScope;
|
||||
import tech.easyflow.approval.entity.ApprovalFlowStep;
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowDetailVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowPageVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowScopeVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
|
||||
import tech.easyflow.approval.enums.ApprovalActionType;
|
||||
import tech.easyflow.approval.enums.ApprovalFlowStatus;
|
||||
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
|
||||
import tech.easyflow.approval.enums.ApprovalResourceType;
|
||||
import tech.easyflow.approval.enums.ApprovalScopeType;
|
||||
import tech.easyflow.approval.mapper.ApprovalFlowMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalFlowScopeMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalFlowStepMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalInstanceMapper;
|
||||
import tech.easyflow.approval.service.ApprovalAssigneeService;
|
||||
import tech.easyflow.approval.service.ApprovalFlowService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 审批流程服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ApprovalFlowServiceImpl extends ServiceImpl<ApprovalFlowMapper, ApprovalFlow> implements ApprovalFlowService {
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowMapper approvalFlowMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowScopeMapper approvalFlowScopeMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowStepMapper approvalFlowStepMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalInstanceMapper approvalInstanceMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalAssigneeService approvalAssigneeService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Page<ApprovalFlowPageVo> pageFlows(String name, String resourceType, String actionType, String status,
|
||||
Long pageNumber, Long pageSize) {
|
||||
long actualPageNumber = pageNumber == null || pageNumber < 1 ? 1L : pageNumber;
|
||||
long actualPageSize = pageSize == null || pageSize < 1 ? 10L : pageSize;
|
||||
QueryWrapper queryWrapper = QueryWrapper.create();
|
||||
if (StringUtils.hasText(name)) {
|
||||
queryWrapper.like(ApprovalFlow::getName, name.trim());
|
||||
}
|
||||
if (StringUtils.hasText(resourceType)) {
|
||||
queryWrapper.eq(ApprovalFlow::getResourceType, ApprovalResourceType.from(resourceType).getCode());
|
||||
}
|
||||
if (StringUtils.hasText(actionType)) {
|
||||
queryWrapper.eq(ApprovalFlow::getActionType, ApprovalActionType.from(actionType).getCode());
|
||||
}
|
||||
if (StringUtils.hasText(status)) {
|
||||
queryWrapper.eq(ApprovalFlow::getStatus, ApprovalFlowStatus.from(status).getCode());
|
||||
}
|
||||
queryWrapper.orderBy("priority desc, modified desc, created asc");
|
||||
|
||||
Page<ApprovalFlow> entityPage = page(new Page<>(actualPageNumber, actualPageSize), queryWrapper);
|
||||
List<ApprovalFlow> records = entityPage.getRecords();
|
||||
List<BigInteger> flowIds = records.stream().map(ApprovalFlow::getId).collect(Collectors.toList());
|
||||
|
||||
Map<BigInteger, List<ApprovalFlowScope>> scopeMap = loadScopeMap(flowIds);
|
||||
Map<BigInteger, List<ApprovalFlowStep>> stepMap = loadStepMap(flowIds);
|
||||
Map<BigInteger, Long> pendingCountMap = loadPendingInstanceCountMap(flowIds);
|
||||
|
||||
List<ApprovalFlowPageVo> result = new ArrayList<>();
|
||||
for (ApprovalFlow record : records) {
|
||||
ApprovalFlowPageVo item = new ApprovalFlowPageVo();
|
||||
item.setId(record.getId());
|
||||
item.setName(record.getName());
|
||||
item.setResourceType(record.getResourceType());
|
||||
item.setActionType(record.getActionType());
|
||||
item.setPriority(record.getPriority());
|
||||
item.setStatus(record.getStatus());
|
||||
item.setVersion(record.getVersion());
|
||||
item.setModified(record.getModified());
|
||||
item.setPendingInstanceCount(pendingCountMap.getOrDefault(record.getId(), 0L));
|
||||
item.setStepCount(stepMap.getOrDefault(record.getId(), List.of()).size());
|
||||
item.setScopeSummary(buildScopeSummary(scopeMap.get(record.getId())));
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
Page<ApprovalFlowPageVo> voPage = new Page<>(actualPageNumber, actualPageSize, entityPage.getTotalRow());
|
||||
voPage.setRecords(result);
|
||||
return voPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ApprovalFlowDetailVo getFlowDetail(BigInteger id) {
|
||||
ApprovalFlow flow = requireFlow(id);
|
||||
ApprovalFlowDetailVo detail = toDetailVo(flow);
|
||||
detail.setPendingInstanceCount(countPendingInstances(id));
|
||||
detail.setDeletable(detail.getPendingInstanceCount() == 0);
|
||||
detail.setScopes(loadScopeVos(id));
|
||||
detail.setSteps(loadStepVos(id));
|
||||
return detail;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public BigInteger saveFlow(ApprovalFlowDetailVo request, BigInteger operatorId) {
|
||||
ApprovalFlowDetailVo normalized = normalizeRequest(request, true);
|
||||
Date now = new Date();
|
||||
ApprovalFlow flow = new ApprovalFlow();
|
||||
flow.setName(normalized.getName());
|
||||
flow.setResourceType(normalized.getResourceType());
|
||||
flow.setActionType(normalized.getActionType());
|
||||
flow.setPriority(normalized.getPriority());
|
||||
flow.setStatus(normalized.getStatus());
|
||||
flow.setVersion(1);
|
||||
flow.setRemark(normalized.getRemark());
|
||||
flow.setCreated(now);
|
||||
flow.setCreatedBy(operatorId);
|
||||
flow.setModified(now);
|
||||
flow.setModifiedBy(operatorId);
|
||||
approvalFlowMapper.insert(flow);
|
||||
replaceChildren(flow.getId(), normalized, operatorId, now);
|
||||
return flow.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateFlow(ApprovalFlowDetailVo request, BigInteger operatorId) {
|
||||
ApprovalFlow existing = requireFlow(request == null ? null : request.getId());
|
||||
ApprovalFlowDetailVo normalized = normalizeRequest(request, false);
|
||||
Date now = new Date();
|
||||
existing.setName(normalized.getName());
|
||||
existing.setResourceType(normalized.getResourceType());
|
||||
existing.setActionType(normalized.getActionType());
|
||||
existing.setPriority(normalized.getPriority());
|
||||
existing.setStatus(normalized.getStatus());
|
||||
existing.setVersion((existing.getVersion() == null ? 1 : existing.getVersion()) + 1);
|
||||
existing.setRemark(normalized.getRemark());
|
||||
existing.setModified(now);
|
||||
existing.setModifiedBy(operatorId);
|
||||
approvalFlowMapper.update(existing);
|
||||
clearChildren(existing.getId());
|
||||
replaceChildren(existing.getId(), normalized, operatorId, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void enableFlow(BigInteger id, BigInteger operatorId) {
|
||||
updateStatus(id, ApprovalFlowStatus.ENABLED.getCode(), operatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void disableFlow(BigInteger id, BigInteger operatorId) {
|
||||
updateStatus(id, ApprovalFlowStatus.DISABLED.getCode(), operatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void removeFlow(BigInteger id) {
|
||||
requireFlow(id);
|
||||
if (countPendingInstances(id) > 0) {
|
||||
throw new BusinessException("存在未完成审批实例,当前流程不允许删除");
|
||||
}
|
||||
clearChildren(id);
|
||||
approvalFlowMapper.deleteById(id);
|
||||
}
|
||||
|
||||
private void updateStatus(BigInteger id, String status, BigInteger operatorId) {
|
||||
ApprovalFlow existing = requireFlow(id);
|
||||
if (ApprovalFlowStatus.ENABLED.getCode().equals(status)) {
|
||||
validateStoredStepAssignees(id);
|
||||
}
|
||||
existing.setStatus(status);
|
||||
existing.setModified(new Date());
|
||||
existing.setModifiedBy(operatorId);
|
||||
approvalFlowMapper.update(existing);
|
||||
}
|
||||
|
||||
private ApprovalFlow requireFlow(BigInteger id) {
|
||||
if (id == null) {
|
||||
throw new BusinessException("流程ID不能为空");
|
||||
}
|
||||
ApprovalFlow flow = approvalFlowMapper.selectOneById(id);
|
||||
if (flow == null) {
|
||||
throw new BusinessException("审批流程不存在");
|
||||
}
|
||||
return flow;
|
||||
}
|
||||
|
||||
private ApprovalFlowDetailVo normalizeRequest(ApprovalFlowDetailVo request, boolean create) {
|
||||
if (request == null) {
|
||||
throw new BusinessException("审批流程不能为空");
|
||||
}
|
||||
if (!create && request.getId() == null) {
|
||||
throw new BusinessException("流程ID不能为空");
|
||||
}
|
||||
if (!StringUtils.hasText(request.getName())) {
|
||||
throw new BusinessException("流程名称不能为空");
|
||||
}
|
||||
if (request.getPriority() == null) {
|
||||
throw new BusinessException("优先级不能为空");
|
||||
}
|
||||
ApprovalFlowDetailVo normalized = new ApprovalFlowDetailVo();
|
||||
normalized.setId(request.getId());
|
||||
normalized.setName(request.getName().trim());
|
||||
normalized.setResourceType(ApprovalResourceType.from(request.getResourceType()).getCode());
|
||||
normalized.setActionType(ApprovalActionType.from(request.getActionType()).getCode());
|
||||
normalized.setPriority(request.getPriority());
|
||||
normalized.setStatus(StringUtils.hasText(request.getStatus())
|
||||
? ApprovalFlowStatus.from(request.getStatus()).getCode()
|
||||
: ApprovalFlowStatus.ENABLED.getCode());
|
||||
normalized.setRemark(request.getRemark());
|
||||
normalized.setScopes(normalizeScopes(request.getScopes()));
|
||||
normalized.setSteps(normalizeSteps(request.getSteps()));
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private List<ApprovalFlowScopeVo> normalizeScopes(List<ApprovalFlowScopeVo> scopes) {
|
||||
if (CollectionUtil.isEmpty(scopes)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
List<ApprovalFlowScopeVo> result = new ArrayList<>();
|
||||
for (ApprovalFlowScopeVo item : scopes) {
|
||||
if (item == null || item.getScopeValue() == null || !StringUtils.hasText(item.getScopeType())) {
|
||||
continue;
|
||||
}
|
||||
ApprovalFlowScopeVo normalized = new ApprovalFlowScopeVo();
|
||||
normalized.setScopeType(ApprovalScopeType.from(item.getScopeType()).getCode());
|
||||
normalized.setScopeValue(item.getScopeValue());
|
||||
normalized.setIncludeChildren(item.getIncludeChildren() != null && item.getIncludeChildren() == 1 ? 1 : 0);
|
||||
String uniqueKey = normalized.getScopeType() + ":" + normalized.getScopeValue();
|
||||
if (keys.add(uniqueKey)) {
|
||||
result.add(normalized);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<ApprovalFlowStepVo> normalizeSteps(List<ApprovalFlowStepVo> steps) {
|
||||
if (CollectionUtil.isEmpty(steps)) {
|
||||
throw new BusinessException("审批步骤不能为空");
|
||||
}
|
||||
List<ApprovalFlowStepVo> result = new ArrayList<>();
|
||||
int index = 1;
|
||||
for (ApprovalFlowStepVo item : steps) {
|
||||
if (item == null || !StringUtils.hasText(item.getStepName())) {
|
||||
continue;
|
||||
}
|
||||
ApprovalFlowStepVo normalized = new ApprovalFlowStepVo();
|
||||
normalized.setStepNo(index++);
|
||||
normalized.setStepName(item.getStepName().trim());
|
||||
normalized.setAssigneeType(item.getAssigneeType());
|
||||
normalized.setAssigneeTargetId(item.getAssigneeTargetId());
|
||||
approvalAssigneeService.normalizeStepAssignee(normalized);
|
||||
result.add(normalized);
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
throw new BusinessException("审批步骤不能为空");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void clearChildren(BigInteger flowId) {
|
||||
approvalFlowScopeMapper.deleteByQuery(QueryWrapper.create().eq(ApprovalFlowScope::getFlowId, flowId));
|
||||
approvalFlowStepMapper.deleteByQuery(QueryWrapper.create().eq(ApprovalFlowStep::getFlowId, flowId));
|
||||
}
|
||||
|
||||
private void replaceChildren(BigInteger flowId, ApprovalFlowDetailVo request, BigInteger operatorId, Date now) {
|
||||
for (ApprovalFlowScopeVo scopeVo : request.getScopes()) {
|
||||
ApprovalFlowScope scope = new ApprovalFlowScope();
|
||||
scope.setFlowId(flowId);
|
||||
scope.setScopeType(scopeVo.getScopeType());
|
||||
scope.setScopeValue(scopeVo.getScopeValue());
|
||||
scope.setIncludeChildren(scopeVo.getIncludeChildren());
|
||||
scope.setCreated(now);
|
||||
scope.setCreatedBy(operatorId);
|
||||
scope.setModified(now);
|
||||
scope.setModifiedBy(operatorId);
|
||||
approvalFlowScopeMapper.insert(scope);
|
||||
}
|
||||
for (ApprovalFlowStepVo stepVo : request.getSteps()) {
|
||||
ApprovalFlowStep step = new ApprovalFlowStep();
|
||||
step.setFlowId(flowId);
|
||||
step.setStepNo(stepVo.getStepNo());
|
||||
step.setStepName(stepVo.getStepName());
|
||||
step.setAssigneeType(stepVo.getAssigneeType());
|
||||
step.setAssigneeTargetId(stepVo.getAssigneeTargetId());
|
||||
step.setAssigneeTargetCode(stepVo.getAssigneeTargetCode());
|
||||
step.setAssigneeTargetName(stepVo.getAssigneeTargetName());
|
||||
step.setCreated(now);
|
||||
step.setCreatedBy(operatorId);
|
||||
step.setModified(now);
|
||||
step.setModifiedBy(operatorId);
|
||||
approvalFlowStepMapper.insert(step);
|
||||
}
|
||||
}
|
||||
|
||||
private ApprovalFlowDetailVo toDetailVo(ApprovalFlow flow) {
|
||||
ApprovalFlowDetailVo detail = new ApprovalFlowDetailVo();
|
||||
detail.setId(flow.getId());
|
||||
detail.setName(flow.getName());
|
||||
detail.setResourceType(flow.getResourceType());
|
||||
detail.setActionType(flow.getActionType());
|
||||
detail.setPriority(flow.getPriority());
|
||||
detail.setStatus(flow.getStatus());
|
||||
detail.setVersion(flow.getVersion());
|
||||
detail.setRemark(flow.getRemark());
|
||||
detail.setCreated(flow.getCreated());
|
||||
detail.setModified(flow.getModified());
|
||||
return detail;
|
||||
}
|
||||
|
||||
private List<ApprovalFlowScopeVo> loadScopeVos(BigInteger flowId) {
|
||||
return approvalFlowScopeMapper.selectListByQuery(QueryWrapper.create().eq(ApprovalFlowScope::getFlowId, flowId))
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(ApprovalFlowScope::getScopeType).thenComparing(ApprovalFlowScope::getScopeValue))
|
||||
.map(item -> {
|
||||
ApprovalFlowScopeVo scopeVo = new ApprovalFlowScopeVo();
|
||||
scopeVo.setId(item.getId());
|
||||
scopeVo.setScopeType(item.getScopeType());
|
||||
scopeVo.setScopeValue(item.getScopeValue());
|
||||
scopeVo.setIncludeChildren(item.getIncludeChildren());
|
||||
return scopeVo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<ApprovalFlowStepVo> loadStepVos(BigInteger flowId) {
|
||||
return approvalFlowStepMapper.selectListByQuery(QueryWrapper.create().eq(ApprovalFlowStep::getFlowId, flowId))
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(ApprovalFlowStep::getStepNo))
|
||||
.map(item -> {
|
||||
ApprovalFlowStepVo stepVo = new ApprovalFlowStepVo();
|
||||
stepVo.setId(item.getId());
|
||||
stepVo.setStepNo(item.getStepNo());
|
||||
stepVo.setStepName(item.getStepName());
|
||||
stepVo.setAssigneeType(item.getAssigneeType());
|
||||
stepVo.setAssigneeTargetId(item.getAssigneeTargetId());
|
||||
stepVo.setAssigneeTargetCode(item.getAssigneeTargetCode());
|
||||
stepVo.setAssigneeTargetName(item.getAssigneeTargetName());
|
||||
return stepVo;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验数据库中已保存步骤的审批对象仍然有效。
|
||||
*
|
||||
* @param flowId 流程ID
|
||||
*/
|
||||
private void validateStoredStepAssignees(BigInteger flowId) {
|
||||
List<ApprovalFlowStepVo> steps = loadStepVos(flowId);
|
||||
if (CollectionUtil.isEmpty(steps)) {
|
||||
throw new BusinessException("审批步骤不能为空");
|
||||
}
|
||||
for (ApprovalFlowStepVo step : steps) {
|
||||
approvalAssigneeService.normalizeStepAssignee(step);
|
||||
}
|
||||
}
|
||||
|
||||
private long countPendingInstances(BigInteger flowId) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(ApprovalInstance::getFlowId, flowId)
|
||||
.in(ApprovalInstance::getStatus, List.of(
|
||||
ApprovalInstanceStatus.PENDING.getCode(),
|
||||
ApprovalInstanceStatus.PROCESSING.getCode()));
|
||||
return approvalInstanceMapper.selectCountByQuery(queryWrapper);
|
||||
}
|
||||
|
||||
private Map<BigInteger, Long> loadPendingInstanceCountMap(List<BigInteger> flowIds) {
|
||||
if (CollectionUtil.isEmpty(flowIds)) {
|
||||
return Map.of();
|
||||
}
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.in(ApprovalInstance::getFlowId, flowIds)
|
||||
.in(ApprovalInstance::getStatus, List.of(
|
||||
ApprovalInstanceStatus.PENDING.getCode(),
|
||||
ApprovalInstanceStatus.PROCESSING.getCode()));
|
||||
List<ApprovalInstance> instances = approvalInstanceMapper.selectListByQuery(queryWrapper);
|
||||
return instances.stream().collect(Collectors.groupingBy(ApprovalInstance::getFlowId, Collectors.counting()));
|
||||
}
|
||||
|
||||
private Map<BigInteger, List<ApprovalFlowScope>> loadScopeMap(List<BigInteger> flowIds) {
|
||||
if (CollectionUtil.isEmpty(flowIds)) {
|
||||
return Map.of();
|
||||
}
|
||||
List<ApprovalFlowScope> scopes = approvalFlowScopeMapper.selectListByQuery(
|
||||
QueryWrapper.create().in(ApprovalFlowScope::getFlowId, flowIds));
|
||||
return scopes.stream().collect(Collectors.groupingBy(
|
||||
ApprovalFlowScope::getFlowId, LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
private Map<BigInteger, List<ApprovalFlowStep>> loadStepMap(List<BigInteger> flowIds) {
|
||||
if (CollectionUtil.isEmpty(flowIds)) {
|
||||
return Map.of();
|
||||
}
|
||||
List<ApprovalFlowStep> steps = approvalFlowStepMapper.selectListByQuery(
|
||||
QueryWrapper.create().in(ApprovalFlowStep::getFlowId, flowIds));
|
||||
return steps.stream().collect(Collectors.groupingBy(
|
||||
ApprovalFlowStep::getFlowId, LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
private String buildScopeSummary(List<ApprovalFlowScope> scopes) {
|
||||
if (CollectionUtil.isEmpty(scopes)) {
|
||||
return "全部";
|
||||
}
|
||||
long categoryCount = scopes.stream()
|
||||
.filter(item -> ApprovalScopeType.CATEGORY.getCode().equals(item.getScopeType()))
|
||||
.count();
|
||||
long deptCount = scopes.stream()
|
||||
.filter(item -> ApprovalScopeType.DEPT.getCode().equals(item.getScopeType()))
|
||||
.count();
|
||||
List<String> parts = new ArrayList<>();
|
||||
if (categoryCount > 0) {
|
||||
parts.add("分类 " + categoryCount);
|
||||
}
|
||||
if (deptCount > 0) {
|
||||
parts.add("部门 " + deptCount);
|
||||
}
|
||||
return String.join(" / ", parts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
package tech.easyflow.approval.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
import tech.easyflow.approval.entity.ApprovalFlowStep;
|
||||
import tech.easyflow.approval.entity.ApprovalLog;
|
||||
import tech.easyflow.approval.entity.ApprovalTask;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowDetailVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
|
||||
import tech.easyflow.approval.service.ApprovalActionFacade;
|
||||
import tech.easyflow.approval.service.ApprovalAssigneeService;
|
||||
import tech.easyflow.approval.enums.ApprovalAssigneeType;
|
||||
import tech.easyflow.approval.enums.ApprovalEventType;
|
||||
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
|
||||
import tech.easyflow.approval.enums.ApprovalTaskStatus;
|
||||
import tech.easyflow.approval.mapper.ApprovalFlowStepMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalInstanceMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalLogMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalTaskMapper;
|
||||
import tech.easyflow.approval.service.ApprovalInstanceService;
|
||||
import tech.easyflow.approval.service.ApprovalMatchService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 审批实例服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ApprovalInstanceServiceImpl implements ApprovalInstanceService {
|
||||
|
||||
@Resource
|
||||
private ApprovalMatchService approvalMatchService;
|
||||
|
||||
@Resource
|
||||
private ApprovalInstanceMapper approvalInstanceMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalTaskMapper approvalTaskMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalLogMapper approvalLogMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowStepMapper approvalFlowStepMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalAssigneeService approvalAssigneeService;
|
||||
|
||||
@Lazy
|
||||
@Resource
|
||||
private ApprovalActionFacade approvalActionFacade;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public BigInteger submitApproval(ApprovalSubmitRequest request) {
|
||||
ApprovalFlowDetailVo flow = approvalMatchService.matchFlow(request);
|
||||
if (CollectionUtil.isEmpty(flow.getSteps())) {
|
||||
throw new BusinessException("审批流程未配置步骤");
|
||||
}
|
||||
if (request.getApplicantId() == null) {
|
||||
throw new BusinessException("申请人不能为空");
|
||||
}
|
||||
|
||||
List<ApprovalFlowStepVo> steps = new ArrayList<>(flow.getSteps());
|
||||
steps.sort(Comparator.comparing(ApprovalFlowStepVo::getStepNo));
|
||||
ApprovalFlowStepVo firstStep = steps.get(0);
|
||||
Date now = new Date();
|
||||
|
||||
ApprovalInstance instance = new ApprovalInstance();
|
||||
instance.setFlowId(flow.getId());
|
||||
instance.setFlowVersion(flow.getVersion());
|
||||
instance.setResourceType(flow.getResourceType());
|
||||
instance.setResourceId(request.getResourceId());
|
||||
instance.setActionType(flow.getActionType());
|
||||
instance.setStatus(ApprovalInstanceStatus.PENDING.getCode());
|
||||
instance.setCurrentStepNo(firstStep.getStepNo());
|
||||
instance.setSnapshotJson(buildInstanceSnapshot(request, flow, steps));
|
||||
instance.setSummary(request.getSummary());
|
||||
instance.setApplicantId(request.getApplicantId());
|
||||
instance.setSubmittedAt(now);
|
||||
instance.setCreated(now);
|
||||
instance.setCreatedBy(request.getApplicantId());
|
||||
instance.setModified(now);
|
||||
instance.setModifiedBy(request.getApplicantId());
|
||||
approvalInstanceMapper.insert(instance);
|
||||
|
||||
createTask(instance.getId(), firstStep, request.getApplicantId(), now);
|
||||
appendLog(instance.getId(), ApprovalEventType.SUBMITTED.getCode(), request.getApplicantId(), Map.of(
|
||||
"flowId", flow.getId(),
|
||||
"flowVersion", flow.getVersion(),
|
||||
"summary", request.getSummary()
|
||||
), now);
|
||||
appendLog(instance.getId(), ApprovalEventType.STEP_CREATED.getCode(), request.getApplicantId(),
|
||||
buildStepCreatedPayload(firstStep), now);
|
||||
return instance.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void approve(BigInteger instanceId, String comment, BigInteger operatorId) {
|
||||
ApprovalInstance instance = requireActiveInstance(instanceId);
|
||||
ApprovalTask currentTask = requireCurrentTask(instanceId, instance.getCurrentStepNo());
|
||||
assertTaskOperable(currentTask, operatorId);
|
||||
List<ApprovalFlowStepVo> steps = resolveFrozenSteps(instance);
|
||||
|
||||
Date now = new Date();
|
||||
finishTask(currentTask, ApprovalTaskStatus.APPROVED.getCode(), comment, operatorId, now);
|
||||
int currentIndex = findCurrentStepIndex(steps, instance.getCurrentStepNo());
|
||||
if (currentIndex == steps.size() - 1) {
|
||||
instance.setStatus(ApprovalInstanceStatus.APPROVED.getCode());
|
||||
instance.setFinishedAt(now);
|
||||
} else {
|
||||
ApprovalFlowStepVo nextStep = steps.get(currentIndex + 1);
|
||||
instance.setStatus(ApprovalInstanceStatus.PROCESSING.getCode());
|
||||
instance.setCurrentStepNo(nextStep.getStepNo());
|
||||
createTask(instance.getId(), nextStep, operatorId, now);
|
||||
appendLog(instance.getId(), ApprovalEventType.STEP_CREATED.getCode(), operatorId,
|
||||
buildStepCreatedPayload(nextStep), now);
|
||||
}
|
||||
instance.setModified(now);
|
||||
instance.setModifiedBy(operatorId);
|
||||
approvalInstanceMapper.update(instance);
|
||||
appendLog(instance.getId(), ApprovalEventType.APPROVED.getCode(), operatorId, Map.of(
|
||||
"stepNo", currentTask.getStepNo(),
|
||||
"comment", comment == null ? "" : comment
|
||||
), now);
|
||||
if (ApprovalInstanceStatus.from(instance.getStatus()).isFinished()) {
|
||||
approvalActionFacade.handleApproved(instance, operatorId, comment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void reject(BigInteger instanceId, String comment, BigInteger operatorId) {
|
||||
ApprovalInstance instance = requireActiveInstance(instanceId);
|
||||
ApprovalTask currentTask = requireCurrentTask(instanceId, instance.getCurrentStepNo());
|
||||
assertTaskOperable(currentTask, operatorId);
|
||||
Date now = new Date();
|
||||
finishTask(currentTask, ApprovalTaskStatus.REJECTED.getCode(), comment, operatorId, now);
|
||||
instance.setStatus(ApprovalInstanceStatus.REJECTED.getCode());
|
||||
instance.setFinishedAt(now);
|
||||
instance.setModified(now);
|
||||
instance.setModifiedBy(operatorId);
|
||||
approvalInstanceMapper.update(instance);
|
||||
appendLog(instance.getId(), ApprovalEventType.REJECTED.getCode(), operatorId, Map.of(
|
||||
"stepNo", currentTask.getStepNo(),
|
||||
"comment", comment == null ? "" : comment
|
||||
), now);
|
||||
approvalActionFacade.handleRejected(instance, operatorId, comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void revoke(BigInteger instanceId, String comment, BigInteger operatorId) {
|
||||
ApprovalInstance instance = requireActiveInstance(instanceId);
|
||||
ApprovalTask currentTask = requireCurrentTask(instanceId, instance.getCurrentStepNo());
|
||||
assertTaskOperable(currentTask, operatorId);
|
||||
Date now = new Date();
|
||||
finishTask(currentTask, ApprovalTaskStatus.REVOKED.getCode(), comment, operatorId, now);
|
||||
instance.setStatus(ApprovalInstanceStatus.REVOKED.getCode());
|
||||
instance.setFinishedAt(now);
|
||||
instance.setModified(now);
|
||||
instance.setModifiedBy(operatorId);
|
||||
approvalInstanceMapper.update(instance);
|
||||
appendLog(instance.getId(), ApprovalEventType.REVOKED.getCode(), operatorId, Map.of(
|
||||
"stepNo", currentTask.getStepNo(),
|
||||
"comment", comment == null ? "" : comment
|
||||
), now);
|
||||
approvalActionFacade.handleRevoked(instance, operatorId, comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean existsActiveInstance(String resourceType, BigInteger resourceId) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(ApprovalInstance::getResourceType, resourceType)
|
||||
.eq(ApprovalInstance::getResourceId, resourceId)
|
||||
.notIn(ApprovalInstance::getStatus,
|
||||
ApprovalInstanceStatus.APPROVED.getCode(),
|
||||
ApprovalInstanceStatus.REJECTED.getCode(),
|
||||
ApprovalInstanceStatus.REVOKED.getCode());
|
||||
return approvalInstanceMapper.selectCountByQuery(queryWrapper) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ApprovalInstance getById(BigInteger instanceId) {
|
||||
return approvalInstanceMapper.selectOneById(instanceId);
|
||||
}
|
||||
|
||||
private Map<String, Object> buildInstanceSnapshot(ApprovalSubmitRequest request, ApprovalFlowDetailVo flow,
|
||||
List<ApprovalFlowStepVo> steps) {
|
||||
Map<String, Object> snapshot = new LinkedHashMap<>();
|
||||
if (request.getSnapshotJson() != null) {
|
||||
snapshot.putAll(request.getSnapshotJson());
|
||||
}
|
||||
snapshot.put("categoryId", request.getCategoryId());
|
||||
snapshot.put("deptId", request.getDeptId());
|
||||
snapshot.put("flowId", flow.getId());
|
||||
snapshot.put("flowVersion", flow.getVersion());
|
||||
snapshot.put("steps", steps.stream().map(item -> {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("stepNo", item.getStepNo());
|
||||
map.put("stepName", item.getStepName());
|
||||
map.put("assigneeType", item.getAssigneeType());
|
||||
map.put("assigneeTargetId", item.getAssigneeTargetId());
|
||||
map.put("assigneeTargetCode", item.getAssigneeTargetCode());
|
||||
map.put("assigneeTargetName", item.getAssigneeTargetName());
|
||||
return map;
|
||||
}).collect(Collectors.toList()));
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private List<ApprovalFlowStepVo> resolveFrozenSteps(ApprovalInstance instance) {
|
||||
if (instance.getSnapshotJson() == null) {
|
||||
throw new BusinessException("审批实例缺少流程快照");
|
||||
}
|
||||
Object value = instance.getSnapshotJson().get("steps");
|
||||
if (!(value instanceof List<?> steps)) {
|
||||
throw new BusinessException("审批实例缺少冻结步骤数据");
|
||||
}
|
||||
List<ApprovalFlowStepVo> result = new ArrayList<>();
|
||||
for (Object step : steps) {
|
||||
if (!(step instanceof Map<?, ?> stepMap)) {
|
||||
continue;
|
||||
}
|
||||
Object stepNo = stepMap.get("stepNo");
|
||||
Object stepName = stepMap.get("stepName");
|
||||
if (!(stepNo instanceof Number) || stepName == null) {
|
||||
continue;
|
||||
}
|
||||
ApprovalFlowStepVo stepVo = new ApprovalFlowStepVo();
|
||||
stepVo.setStepNo(((Number) stepNo).intValue());
|
||||
stepVo.setStepName(String.valueOf(stepName));
|
||||
Object assigneeType = stepMap.get("assigneeType");
|
||||
Object assigneeTargetId = stepMap.get("assigneeTargetId");
|
||||
Object assigneeTargetCode = stepMap.get("assigneeTargetCode");
|
||||
Object assigneeTargetName = stepMap.get("assigneeTargetName");
|
||||
if (assigneeType instanceof String type) {
|
||||
stepVo.setAssigneeType(type);
|
||||
}
|
||||
if (assigneeTargetId instanceof Number number) {
|
||||
stepVo.setAssigneeTargetId(BigInteger.valueOf(number.longValue()));
|
||||
} else if (assigneeTargetId instanceof String string && !string.isBlank()) {
|
||||
stepVo.setAssigneeTargetId(new BigInteger(string));
|
||||
}
|
||||
if (assigneeTargetCode != null) {
|
||||
stepVo.setAssigneeTargetCode(String.valueOf(assigneeTargetCode));
|
||||
}
|
||||
if (assigneeTargetName != null) {
|
||||
stepVo.setAssigneeTargetName(String.valueOf(assigneeTargetName));
|
||||
}
|
||||
result.add(stepVo);
|
||||
}
|
||||
mergeStepAssigneeFromFlow(instance.getFlowId(), result);
|
||||
result.sort(Comparator.comparing(ApprovalFlowStepVo::getStepNo));
|
||||
if (result.isEmpty()) {
|
||||
throw new BusinessException("审批实例缺少冻结步骤数据");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void createTask(BigInteger instanceId, ApprovalFlowStepVo step, BigInteger operatorId, Date now) {
|
||||
ApprovalTask task = new ApprovalTask();
|
||||
task.setInstanceId(instanceId);
|
||||
task.setStepNo(step.getStepNo());
|
||||
task.setStatus(ApprovalTaskStatus.PENDING.getCode());
|
||||
task.setAssigneeRoleCode(ApprovalAssigneeType.ROLE.getCode().equals(step.getAssigneeType())
|
||||
? step.getAssigneeTargetCode()
|
||||
: null);
|
||||
task.setAssigneeType(step.getAssigneeType());
|
||||
task.setAssigneeTargetId(step.getAssigneeTargetId());
|
||||
task.setAssigneeTargetCode(step.getAssigneeTargetCode());
|
||||
task.setAssigneeTargetName(step.getAssigneeTargetName());
|
||||
task.setCreated(now);
|
||||
task.setCreatedBy(operatorId);
|
||||
task.setModified(now);
|
||||
task.setModifiedBy(operatorId);
|
||||
approvalTaskMapper.insert(task);
|
||||
}
|
||||
|
||||
private void finishTask(ApprovalTask task, String status, String comment, BigInteger operatorId, Date now) {
|
||||
task.setStatus(status);
|
||||
task.setComment(comment);
|
||||
task.setActedBy(operatorId);
|
||||
task.setActedAt(now);
|
||||
task.setModified(now);
|
||||
task.setModifiedBy(operatorId);
|
||||
approvalTaskMapper.update(task);
|
||||
}
|
||||
|
||||
private void appendLog(BigInteger instanceId, String eventType, BigInteger operatorId, Map<String, Object> payload, Date now) {
|
||||
ApprovalLog log = new ApprovalLog();
|
||||
log.setInstanceId(instanceId);
|
||||
log.setEventType(eventType);
|
||||
log.setOperatorId(operatorId);
|
||||
log.setPayloadJson(new LinkedHashMap<>(payload));
|
||||
log.setCreated(now);
|
||||
log.setCreatedBy(operatorId);
|
||||
log.setModified(now);
|
||||
log.setModifiedBy(operatorId);
|
||||
approvalLogMapper.insert(log);
|
||||
}
|
||||
|
||||
private ApprovalInstance requireActiveInstance(BigInteger instanceId) {
|
||||
if (instanceId == null) {
|
||||
throw new BusinessException("审批实例ID不能为空");
|
||||
}
|
||||
ApprovalInstance instance = approvalInstanceMapper.selectOneById(instanceId);
|
||||
if (instance == null) {
|
||||
throw new BusinessException("审批实例不存在");
|
||||
}
|
||||
if (ApprovalInstanceStatus.from(instance.getStatus()).isFinished()) {
|
||||
throw new BusinessException("审批实例已结束,无法继续处理");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ApprovalTask requireCurrentTask(BigInteger instanceId, Integer stepNo) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create()
|
||||
.eq(ApprovalTask::getInstanceId, instanceId)
|
||||
.eq(ApprovalTask::getStepNo, stepNo)
|
||||
.eq(ApprovalTask::getStatus, ApprovalTaskStatus.PENDING.getCode());
|
||||
ApprovalTask task = approvalTaskMapper.selectOneByQuery(queryWrapper);
|
||||
if (task == null) {
|
||||
throw new BusinessException("当前审批任务不存在");
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
private int findCurrentStepIndex(List<ApprovalFlowStepVo> steps, Integer currentStepNo) {
|
||||
for (int i = 0; i < steps.size(); i++) {
|
||||
if (steps.get(i).getStepNo().equals(currentStepNo)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new BusinessException("审批流程步骤不存在");
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验当前操作人是否命中审批任务。
|
||||
*
|
||||
* @param task 审批任务
|
||||
* @param operatorId 当前操作人
|
||||
*/
|
||||
private void assertTaskOperable(ApprovalTask task, BigInteger operatorId) {
|
||||
if (!approvalAssigneeService.canHandleTask(task, operatorId, approvalAssigneeService.getAvailableRoleIds(operatorId))) {
|
||||
throw new BusinessException("当前用户无权处理该审批任务");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为历史审批实例补齐流程步骤里的审批对象信息。
|
||||
*
|
||||
* @param flowId 流程ID
|
||||
* @param steps 冻结步骤
|
||||
*/
|
||||
private void mergeStepAssigneeFromFlow(BigInteger flowId, List<ApprovalFlowStepVo> steps) {
|
||||
List<ApprovalFlowStep> storedSteps = approvalFlowStepMapper.selectListByQuery(
|
||||
QueryWrapper.create().eq(ApprovalFlowStep::getFlowId, flowId));
|
||||
if (CollectionUtil.isEmpty(storedSteps)) {
|
||||
return;
|
||||
}
|
||||
Map<Integer, ApprovalFlowStep> storedMap = storedSteps.stream().collect(Collectors.toMap(
|
||||
ApprovalFlowStep::getStepNo,
|
||||
item -> item,
|
||||
(left, right) -> left,
|
||||
LinkedHashMap::new
|
||||
));
|
||||
for (ApprovalFlowStepVo step : steps) {
|
||||
if (step.getAssigneeTargetId() != null && step.getAssigneeType() != null) {
|
||||
continue;
|
||||
}
|
||||
ApprovalFlowStep storedStep = storedMap.get(step.getStepNo());
|
||||
if (storedStep == null) {
|
||||
continue;
|
||||
}
|
||||
step.setAssigneeType(storedStep.getAssigneeType());
|
||||
step.setAssigneeTargetId(storedStep.getAssigneeTargetId());
|
||||
step.setAssigneeTargetCode(storedStep.getAssigneeTargetCode());
|
||||
step.setAssigneeTargetName(storedStep.getAssigneeTargetName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装步骤创建日志载荷。
|
||||
*
|
||||
* @param step 步骤信息
|
||||
* @return 日志载荷
|
||||
*/
|
||||
private Map<String, Object> buildStepCreatedPayload(ApprovalFlowStepVo step) {
|
||||
Map<String, Object> payload = new LinkedHashMap<>();
|
||||
payload.put("stepNo", step.getStepNo());
|
||||
payload.put("stepName", step.getStepName());
|
||||
payload.put("assigneeType", step.getAssigneeType());
|
||||
payload.put("assigneeTargetId", step.getAssigneeTargetId());
|
||||
payload.put("assigneeTargetCode", step.getAssigneeTargetCode());
|
||||
payload.put("assigneeTargetName", step.getAssigneeTargetName());
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package tech.easyflow.approval.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.approval.entity.ApprovalFlow;
|
||||
import tech.easyflow.approval.entity.ApprovalFlowScope;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowDetailVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
|
||||
import tech.easyflow.approval.enums.ApprovalActionType;
|
||||
import tech.easyflow.approval.enums.ApprovalFlowStatus;
|
||||
import tech.easyflow.approval.enums.ApprovalResourceType;
|
||||
import tech.easyflow.approval.enums.ApprovalScopeType;
|
||||
import tech.easyflow.approval.mapper.ApprovalFlowMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalFlowScopeMapper;
|
||||
import tech.easyflow.approval.service.ApprovalFlowService;
|
||||
import tech.easyflow.approval.service.ApprovalMatchService;
|
||||
import tech.easyflow.system.entity.SysDept;
|
||||
import tech.easyflow.system.service.SysDeptService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 审批流程匹配服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ApprovalMatchServiceImpl implements ApprovalMatchService {
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowMapper approvalFlowMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowScopeMapper approvalFlowScopeMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowService approvalFlowService;
|
||||
|
||||
@Resource
|
||||
private SysDeptService sysDeptService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ApprovalFlowDetailVo matchFlow(ApprovalSubmitRequest request) {
|
||||
ApprovalSubmitRequest normalized = normalizeRequest(request);
|
||||
QueryWrapper flowWrapper = QueryWrapper.create()
|
||||
.eq(ApprovalFlow::getResourceType, normalized.getResourceType())
|
||||
.eq(ApprovalFlow::getActionType, normalized.getActionType())
|
||||
.eq(ApprovalFlow::getStatus, ApprovalFlowStatus.ENABLED.getCode());
|
||||
List<ApprovalFlow> flows = approvalFlowMapper.selectListByQuery(flowWrapper);
|
||||
if (CollectionUtil.isEmpty(flows)) {
|
||||
throw new BusinessException("未找到可用的审批流程");
|
||||
}
|
||||
List<BigInteger> flowIds = flows.stream().map(ApprovalFlow::getId).collect(Collectors.toList());
|
||||
Map<BigInteger, List<ApprovalFlowScope>> scopeMap = approvalFlowScopeMapper.selectListByQuery(
|
||||
QueryWrapper.create().in(ApprovalFlowScope::getFlowId, flowIds))
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(ApprovalFlowScope::getFlowId, LinkedHashMap::new, Collectors.toList()));
|
||||
|
||||
List<MatchedFlow> matchedFlows = new ArrayList<>();
|
||||
for (ApprovalFlow flow : flows) {
|
||||
List<ApprovalFlowScope> scopes = scopeMap.getOrDefault(flow.getId(), List.of());
|
||||
if (matches(scopes, normalized)) {
|
||||
matchedFlows.add(new MatchedFlow(flow, computeSpecificity(scopes)));
|
||||
}
|
||||
}
|
||||
if (matchedFlows.isEmpty()) {
|
||||
throw new BusinessException("当前资源上下文未命中审批流程");
|
||||
}
|
||||
|
||||
matchedFlows.sort(Comparator
|
||||
.comparing((MatchedFlow item) -> item.flow.getPriority(), Comparator.nullsLast(Integer::compareTo)).reversed()
|
||||
.thenComparing(MatchedFlow::getSpecificity, Comparator.reverseOrder())
|
||||
.thenComparing(item -> item.flow.getCreated(), Comparator.nullsLast(Comparator.naturalOrder())));
|
||||
|
||||
MatchedFlow first = matchedFlows.get(0);
|
||||
if (matchedFlows.size() > 1) {
|
||||
MatchedFlow second = matchedFlows.get(1);
|
||||
boolean samePriority = Objects.equals(first.flow.getPriority(), second.flow.getPriority());
|
||||
boolean sameSpecificity = Objects.equals(first.specificity, second.specificity);
|
||||
boolean sameCreated = Objects.equals(first.flow.getCreated(), second.flow.getCreated());
|
||||
if (samePriority && sameSpecificity && sameCreated) {
|
||||
throw new BusinessException("审批流程匹配冲突,请调整优先级或范围配置");
|
||||
}
|
||||
}
|
||||
return approvalFlowService.getFlowDetail(first.flow.getId());
|
||||
}
|
||||
|
||||
private ApprovalSubmitRequest normalizeRequest(ApprovalSubmitRequest request) {
|
||||
if (request == null) {
|
||||
throw new BusinessException("审批请求不能为空");
|
||||
}
|
||||
if (request.getResourceId() == null) {
|
||||
throw new BusinessException("资源ID不能为空");
|
||||
}
|
||||
ApprovalSubmitRequest normalized = new ApprovalSubmitRequest();
|
||||
normalized.setResourceType(ApprovalResourceType.from(request.getResourceType()).getCode());
|
||||
normalized.setActionType(ApprovalActionType.from(request.getActionType()).getCode());
|
||||
normalized.setResourceId(request.getResourceId());
|
||||
normalized.setApplicantId(request.getApplicantId());
|
||||
normalized.setCategoryId(request.getCategoryId());
|
||||
normalized.setDeptId(request.getDeptId());
|
||||
normalized.setSummary(request.getSummary());
|
||||
normalized.setSnapshotJson(request.getSnapshotJson());
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private boolean matches(List<ApprovalFlowScope> scopes, ApprovalSubmitRequest request) {
|
||||
List<ApprovalFlowScope> categoryScopes = scopes.stream()
|
||||
.filter(item -> ApprovalScopeType.CATEGORY.getCode().equals(item.getScopeType()))
|
||||
.collect(Collectors.toList());
|
||||
List<ApprovalFlowScope> deptScopes = scopes.stream()
|
||||
.filter(item -> ApprovalScopeType.DEPT.getCode().equals(item.getScopeType()))
|
||||
.collect(Collectors.toList());
|
||||
return matchesCategory(categoryScopes, request.getCategoryId()) && matchesDept(deptScopes, request.getDeptId());
|
||||
}
|
||||
|
||||
private boolean matchesCategory(List<ApprovalFlowScope> scopes, BigInteger categoryId) {
|
||||
if (CollectionUtil.isEmpty(scopes)) {
|
||||
return true;
|
||||
}
|
||||
if (categoryId == null) {
|
||||
return false;
|
||||
}
|
||||
return scopes.stream().anyMatch(item -> categoryId.equals(item.getScopeValue()));
|
||||
}
|
||||
|
||||
private boolean matchesDept(List<ApprovalFlowScope> scopes, BigInteger deptId) {
|
||||
if (CollectionUtil.isEmpty(scopes)) {
|
||||
return true;
|
||||
}
|
||||
if (deptId == null) {
|
||||
return false;
|
||||
}
|
||||
SysDept dept = sysDeptService.getById(deptId);
|
||||
String ancestors = dept == null ? "" : dept.getAncestors();
|
||||
List<BigInteger> ancestorIds = new ArrayList<>();
|
||||
if (StringUtils.hasText(ancestors)) {
|
||||
for (String ancestor : ancestors.split(",")) {
|
||||
if (!StringUtils.hasText(ancestor) || "0".equals(ancestor.trim())) {
|
||||
continue;
|
||||
}
|
||||
ancestorIds.add(new BigInteger(ancestor.trim()));
|
||||
}
|
||||
}
|
||||
return scopes.stream().anyMatch(item -> {
|
||||
if (deptId.equals(item.getScopeValue())) {
|
||||
return true;
|
||||
}
|
||||
return item.getIncludeChildren() != null
|
||||
&& item.getIncludeChildren() == 1
|
||||
&& ancestorIds.contains(item.getScopeValue());
|
||||
});
|
||||
}
|
||||
|
||||
private int computeSpecificity(List<ApprovalFlowScope> scopes) {
|
||||
int score = scopes.size();
|
||||
boolean hasCategory = scopes.stream().anyMatch(item -> ApprovalScopeType.CATEGORY.getCode().equals(item.getScopeType()));
|
||||
boolean hasDept = scopes.stream().anyMatch(item -> ApprovalScopeType.DEPT.getCode().equals(item.getScopeType()));
|
||||
if (hasCategory) {
|
||||
score += 10;
|
||||
}
|
||||
if (hasDept) {
|
||||
score += 10;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
private static class MatchedFlow {
|
||||
private final ApprovalFlow flow;
|
||||
private final Integer specificity;
|
||||
|
||||
private MatchedFlow(ApprovalFlow flow, Integer specificity) {
|
||||
this.flow = flow;
|
||||
this.specificity = specificity;
|
||||
}
|
||||
|
||||
public Integer getSpecificity() {
|
||||
return specificity;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
package tech.easyflow.approval.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.mybatisflex.core.paginate.Page;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import tech.easyflow.common.entity.LoginAccount;
|
||||
import tech.easyflow.common.satoken.util.SaTokenUtil;
|
||||
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||
import tech.easyflow.approval.entity.ApprovalFlowStep;
|
||||
import tech.easyflow.approval.entity.ApprovalInstance;
|
||||
import tech.easyflow.approval.entity.ApprovalLog;
|
||||
import tech.easyflow.approval.entity.ApprovalTask;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalInstanceDetailVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalInstancePageVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalFlowStepVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalLogVo;
|
||||
import tech.easyflow.approval.entity.vo.ApprovalTaskVo;
|
||||
import tech.easyflow.approval.enums.ApprovalActionType;
|
||||
import tech.easyflow.approval.enums.ApprovalInstanceStatus;
|
||||
import tech.easyflow.approval.enums.ApprovalResourceType;
|
||||
import tech.easyflow.approval.enums.ApprovalTaskStatus;
|
||||
import tech.easyflow.approval.mapper.ApprovalFlowStepMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalInstanceMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalLogMapper;
|
||||
import tech.easyflow.approval.mapper.ApprovalTaskMapper;
|
||||
import tech.easyflow.approval.service.ApprovalAssigneeService;
|
||||
import tech.easyflow.approval.service.ApprovalQueryService;
|
||||
import tech.easyflow.system.entity.SysAccount;
|
||||
import tech.easyflow.system.service.SysAccountService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 审批查询服务实现。
|
||||
*/
|
||||
@Service
|
||||
public class ApprovalQueryServiceImpl implements ApprovalQueryService {
|
||||
|
||||
@Resource
|
||||
private ApprovalInstanceMapper approvalInstanceMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalTaskMapper approvalTaskMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalLogMapper approvalLogMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalFlowStepMapper approvalFlowStepMapper;
|
||||
|
||||
@Resource
|
||||
private ApprovalAssigneeService approvalAssigneeService;
|
||||
|
||||
@Resource
|
||||
private SysAccountService sysAccountService;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Page<ApprovalInstancePageVo> pendingPage(String resourceType, String actionType, String keyword,
|
||||
Long pageNumber, Long pageSize) {
|
||||
LoginAccount account = requireLoginAccount();
|
||||
Set<BigInteger> roleIds = approvalAssigneeService.getAvailableRoleIds(account.getId());
|
||||
Set<BigInteger> instanceIds = approvalAssigneeService.listPendingInstanceIds(account.getId(), roleIds, null);
|
||||
if (CollectionUtil.isEmpty(instanceIds)) {
|
||||
return new Page<>(List.of(), safePageNumber(pageNumber), safePageSize(pageSize), 0L);
|
||||
}
|
||||
QueryWrapper queryWrapper = buildBaseQuery(resourceType, actionType, keyword);
|
||||
queryWrapper.in(ApprovalInstance::getId, instanceIds);
|
||||
queryWrapper.in(ApprovalInstance::getStatus, List.of(
|
||||
ApprovalInstanceStatus.PENDING.getCode(),
|
||||
ApprovalInstanceStatus.PROCESSING.getCode()));
|
||||
queryWrapper.orderBy("submitted_at desc, id desc");
|
||||
return mapPage(queryWrapper, safePageNumber(pageNumber), safePageSize(pageSize), true, account, roleIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Page<ApprovalInstancePageVo> processedPage(String resourceType, String actionType, String keyword,
|
||||
Long pageNumber, Long pageSize) {
|
||||
LoginAccount account = requireLoginAccount();
|
||||
QueryWrapper actedTaskWrapper = QueryWrapper.create()
|
||||
.eq(ApprovalTask::getActedBy, account.getId())
|
||||
.in(ApprovalTask::getStatus, List.of(
|
||||
ApprovalTaskStatus.APPROVED.getCode(),
|
||||
ApprovalTaskStatus.REJECTED.getCode(),
|
||||
ApprovalTaskStatus.REVOKED.getCode()));
|
||||
List<BigInteger> instanceIds = approvalTaskMapper.selectListByQuery(actedTaskWrapper).stream()
|
||||
.map(ApprovalTask::getInstanceId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (CollectionUtil.isEmpty(instanceIds)) {
|
||||
return new Page<>(List.of(), safePageNumber(pageNumber), safePageSize(pageSize), 0L);
|
||||
}
|
||||
QueryWrapper queryWrapper = buildBaseQuery(resourceType, actionType, keyword);
|
||||
queryWrapper.in(ApprovalInstance::getId, instanceIds);
|
||||
queryWrapper.orderBy("finished_at desc, id desc");
|
||||
return mapPage(queryWrapper, safePageNumber(pageNumber), safePageSize(pageSize), false, account, Set.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Page<ApprovalInstancePageVo> initiatedPage(String resourceType, String actionType, String keyword,
|
||||
Long pageNumber, Long pageSize) {
|
||||
LoginAccount account = requireLoginAccount();
|
||||
QueryWrapper queryWrapper = buildBaseQuery(resourceType, actionType, keyword);
|
||||
queryWrapper.eq(ApprovalInstance::getApplicantId, account.getId());
|
||||
queryWrapper.orderBy("submitted_at desc, id desc");
|
||||
return mapPage(queryWrapper, safePageNumber(pageNumber), safePageSize(pageSize), false, account, Set.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public ApprovalInstanceDetailVo detail(BigInteger instanceId) {
|
||||
ApprovalInstance instance = approvalInstanceMapper.selectOneById(instanceId);
|
||||
if (instance == null) {
|
||||
throw new BusinessException("审批实例不存在");
|
||||
}
|
||||
ApprovalInstanceDetailVo detail = new ApprovalInstanceDetailVo();
|
||||
detail.setId(instance.getId());
|
||||
detail.setFlowId(instance.getFlowId());
|
||||
detail.setFlowVersion(instance.getFlowVersion());
|
||||
detail.setResourceType(instance.getResourceType());
|
||||
detail.setResourceId(instance.getResourceId());
|
||||
detail.setActionType(instance.getActionType());
|
||||
detail.setStatus(instance.getStatus());
|
||||
detail.setCurrentStepNo(instance.getCurrentStepNo());
|
||||
detail.setSummary(instance.getSummary());
|
||||
detail.setApplicantId(instance.getApplicantId());
|
||||
detail.setSubmittedAt(instance.getSubmittedAt());
|
||||
detail.setFinishedAt(instance.getFinishedAt());
|
||||
detail.setSnapshotJson(instance.getSnapshotJson());
|
||||
|
||||
List<ApprovalTask> tasks = approvalTaskMapper.selectListByQuery(
|
||||
QueryWrapper.create().eq(ApprovalTask::getInstanceId, instanceId));
|
||||
List<ApprovalLog> logs = approvalLogMapper.selectListByQuery(
|
||||
QueryWrapper.create().eq(ApprovalLog::getInstanceId, instanceId));
|
||||
Map<Integer, ApprovalFlowStepVo> frozenStepMap = resolveFrozenStepMap(instance);
|
||||
Map<BigInteger, String> accountNameMap = loadAccountNameMap(instance, tasks, logs);
|
||||
detail.setApplicantName(accountNameMap.get(instance.getApplicantId()));
|
||||
|
||||
detail.setTasks(tasks.stream()
|
||||
.sorted(Comparator.comparing(ApprovalTask::getStepNo))
|
||||
.map(item -> {
|
||||
ApprovalTaskVo taskVo = new ApprovalTaskVo();
|
||||
taskVo.setId(item.getId());
|
||||
taskVo.setStepNo(item.getStepNo());
|
||||
taskVo.setStepName(resolveStepName(frozenStepMap, item.getStepNo()));
|
||||
taskVo.setStatus(item.getStatus());
|
||||
taskVo.setAssigneeRoleCode(item.getAssigneeRoleCode());
|
||||
taskVo.setAssigneeType(item.getAssigneeType());
|
||||
taskVo.setAssigneeTargetId(item.getAssigneeTargetId());
|
||||
taskVo.setAssigneeTargetCode(item.getAssigneeTargetCode());
|
||||
taskVo.setAssigneeTargetName(item.getAssigneeTargetName());
|
||||
taskVo.setActedBy(item.getActedBy());
|
||||
taskVo.setActedByName(accountNameMap.get(item.getActedBy()));
|
||||
taskVo.setActedAt(item.getActedAt());
|
||||
taskVo.setComment(item.getComment());
|
||||
return taskVo;
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
detail.setLogs(logs.stream()
|
||||
.sorted(Comparator.comparing(ApprovalLog::getCreated))
|
||||
.map(item -> {
|
||||
ApprovalLogVo logVo = new ApprovalLogVo();
|
||||
logVo.setId(item.getId());
|
||||
logVo.setEventType(item.getEventType());
|
||||
logVo.setOperatorId(item.getOperatorId());
|
||||
logVo.setOperatorName(accountNameMap.get(item.getOperatorId()));
|
||||
logVo.setCreated(item.getCreated());
|
||||
logVo.setPayloadJson(item.getPayloadJson());
|
||||
return logVo;
|
||||
})
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
LoginAccount account = requireLoginAccount();
|
||||
Set<BigInteger> roleIds = approvalAssigneeService.getAvailableRoleIds(account.getId());
|
||||
boolean canOperate = !ApprovalInstanceStatus.from(instance.getStatus()).isFinished()
|
||||
&& tasks.stream().anyMatch(item -> item.getStepNo().equals(instance.getCurrentStepNo())
|
||||
&& ApprovalTaskStatus.PENDING.getCode().equals(item.getStatus())
|
||||
&& approvalAssigneeService.canHandleTask(item, account.getId(), roleIds));
|
||||
detail.setCanApprove(canOperate);
|
||||
detail.setCanReject(canOperate);
|
||||
detail.setCanRevoke(canOperate);
|
||||
return detail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量加载审批详情里涉及到的账号显示名称。
|
||||
*
|
||||
* @param instance 审批实例
|
||||
* @param tasks 审批任务列表
|
||||
* @param logs 审批日志列表
|
||||
* @return 账号 ID 到展示名称的映射
|
||||
*/
|
||||
private Map<BigInteger, String> loadAccountNameMap(ApprovalInstance instance, List<ApprovalTask> tasks,
|
||||
List<ApprovalLog> logs) {
|
||||
Set<BigInteger> accountIds = new HashSet<>();
|
||||
if (instance.getApplicantId() != null) {
|
||||
accountIds.add(instance.getApplicantId());
|
||||
}
|
||||
tasks.stream()
|
||||
.map(ApprovalTask::getActedBy)
|
||||
.filter(id -> id != null)
|
||||
.forEach(accountIds::add);
|
||||
logs.stream()
|
||||
.map(ApprovalLog::getOperatorId)
|
||||
.filter(id -> id != null)
|
||||
.forEach(accountIds::add);
|
||||
if (CollectionUtil.isEmpty(accountIds)) {
|
||||
return Map.of();
|
||||
}
|
||||
return sysAccountService.listByIds(accountIds).stream()
|
||||
.collect(Collectors.toMap(
|
||||
SysAccount::getId,
|
||||
this::resolveAccountName,
|
||||
(left, right) -> left,
|
||||
LinkedHashMap::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析账号展示名称,优先昵称,其次登录名。
|
||||
*
|
||||
* @param account 账号实体
|
||||
* @return 展示名称
|
||||
*/
|
||||
private String resolveAccountName(SysAccount account) {
|
||||
if (account == null) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.hasText(account.getNickname())) {
|
||||
return account.getNickname().trim();
|
||||
}
|
||||
if (StringUtils.hasText(account.getLoginName())) {
|
||||
return account.getLoginName().trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private QueryWrapper buildBaseQuery(String resourceType, String actionType, String keyword) {
|
||||
QueryWrapper queryWrapper = QueryWrapper.create();
|
||||
if (StringUtils.hasText(resourceType)) {
|
||||
queryWrapper.eq(ApprovalInstance::getResourceType, ApprovalResourceType.from(resourceType).getCode());
|
||||
}
|
||||
if (StringUtils.hasText(actionType)) {
|
||||
queryWrapper.eq(ApprovalInstance::getActionType, ApprovalActionType.from(actionType).getCode());
|
||||
}
|
||||
if (StringUtils.hasText(keyword)) {
|
||||
queryWrapper.like(ApprovalInstance::getSummary, keyword.trim());
|
||||
}
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
private Page<ApprovalInstancePageVo> mapPage(QueryWrapper queryWrapper, long pageNumber, long pageSize,
|
||||
boolean pendingMode, LoginAccount account, Set<BigInteger> roleIds) {
|
||||
Page<ApprovalInstance> page = approvalInstanceMapper.paginate(pageNumber, pageSize, queryWrapper);
|
||||
List<ApprovalInstance> records = page.getRecords();
|
||||
Set<BigInteger> pendingTaskInstanceIds = pendingMode
|
||||
? approvalAssigneeService.listPendingInstanceIds(account.getId(), roleIds,
|
||||
records.stream().map(ApprovalInstance::getId).collect(Collectors.toList()))
|
||||
: Set.of();
|
||||
|
||||
List<ApprovalInstancePageVo> result = new ArrayList<>();
|
||||
for (ApprovalInstance record : records) {
|
||||
ApprovalInstancePageVo item = new ApprovalInstancePageVo();
|
||||
item.setId(record.getId());
|
||||
item.setResourceType(record.getResourceType());
|
||||
item.setResourceId(record.getResourceId());
|
||||
item.setActionType(record.getActionType());
|
||||
item.setStatus(record.getStatus());
|
||||
item.setCurrentStepNo(record.getCurrentStepNo());
|
||||
item.setCurrentStepName(resolveCurrentStepName(record));
|
||||
item.setSummary(record.getSummary());
|
||||
item.setApplicantId(record.getApplicantId());
|
||||
item.setSubmittedAt(record.getSubmittedAt());
|
||||
item.setFinishedAt(record.getFinishedAt());
|
||||
boolean canOperate = pendingMode
|
||||
&& pendingTaskInstanceIds.contains(record.getId())
|
||||
&& !ApprovalInstanceStatus.from(record.getStatus()).isFinished();
|
||||
item.setCanApprove(canOperate);
|
||||
item.setCanReject(canOperate);
|
||||
item.setCanRevoke(canOperate);
|
||||
result.add(item);
|
||||
}
|
||||
|
||||
Page<ApprovalInstancePageVo> voPage = new Page<>(pageNumber, pageSize, page.getTotalRow());
|
||||
voPage.setRecords(result);
|
||||
return voPage;
|
||||
}
|
||||
|
||||
private long safePageNumber(Long pageNumber) {
|
||||
return pageNumber == null || pageNumber < 1 ? 1L : pageNumber;
|
||||
}
|
||||
|
||||
private long safePageSize(Long pageSize) {
|
||||
return pageSize == null || pageSize < 1 ? 10L : pageSize;
|
||||
}
|
||||
|
||||
private LoginAccount requireLoginAccount() {
|
||||
LoginAccount account = SaTokenUtil.getLoginAccount();
|
||||
if (account == null) {
|
||||
throw new BusinessException("当前未登录");
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
private String resolveCurrentStepName(ApprovalInstance instance) {
|
||||
Map<Integer, ApprovalFlowStepVo> stepMap = resolveFrozenStepMap(instance);
|
||||
return resolveStepName(stepMap, instance.getCurrentStepNo());
|
||||
}
|
||||
|
||||
private String resolveStepName(Map<Integer, ApprovalFlowStepVo> stepMap, Integer stepNo) {
|
||||
ApprovalFlowStepVo step = stepMap.get(stepNo);
|
||||
return step == null ? null : step.getStepName();
|
||||
}
|
||||
|
||||
private Map<Integer, ApprovalFlowStepVo> resolveFrozenStepMap(ApprovalInstance instance) {
|
||||
Map<Integer, ApprovalFlowStepVo> result = new LinkedHashMap<>();
|
||||
Object steps = instance.getSnapshotJson() == null ? null : instance.getSnapshotJson().get("steps");
|
||||
if (steps instanceof List<?> frozenSteps) {
|
||||
for (Object item : frozenSteps) {
|
||||
if (!(item instanceof Map<?, ?> map)) {
|
||||
continue;
|
||||
}
|
||||
Object stepNo = map.get("stepNo");
|
||||
Object stepName = map.get("stepName");
|
||||
if (!(stepNo instanceof Number) || stepName == null) {
|
||||
continue;
|
||||
}
|
||||
ApprovalFlowStepVo stepVo = new ApprovalFlowStepVo();
|
||||
stepVo.setStepNo(((Number) stepNo).intValue());
|
||||
stepVo.setStepName(String.valueOf(stepName));
|
||||
Object assigneeType = map.get("assigneeType");
|
||||
Object assigneeTargetId = map.get("assigneeTargetId");
|
||||
Object assigneeTargetCode = map.get("assigneeTargetCode");
|
||||
Object assigneeTargetName = map.get("assigneeTargetName");
|
||||
if (assigneeType != null) {
|
||||
stepVo.setAssigneeType(String.valueOf(assigneeType));
|
||||
}
|
||||
if (assigneeTargetId instanceof Number number) {
|
||||
stepVo.setAssigneeTargetId(BigInteger.valueOf(number.longValue()));
|
||||
} else if (assigneeTargetId instanceof String string && StringUtils.hasText(string)) {
|
||||
stepVo.setAssigneeTargetId(new BigInteger(string));
|
||||
}
|
||||
if (assigneeTargetCode != null) {
|
||||
stepVo.setAssigneeTargetCode(String.valueOf(assigneeTargetCode));
|
||||
}
|
||||
if (assigneeTargetName != null) {
|
||||
stepVo.setAssigneeTargetName(String.valueOf(assigneeTargetName));
|
||||
}
|
||||
result.put(stepVo.getStepNo(), stepVo);
|
||||
}
|
||||
}
|
||||
if (!result.isEmpty() && result.values().stream().allMatch(item -> item.getAssigneeType() != null && item.getAssigneeTargetId() != null)) {
|
||||
return result;
|
||||
}
|
||||
List<ApprovalFlowStep> storedSteps = approvalFlowStepMapper.selectListByQuery(
|
||||
QueryWrapper.create().eq(ApprovalFlowStep::getFlowId, instance.getFlowId()));
|
||||
for (ApprovalFlowStep step : storedSteps) {
|
||||
ApprovalFlowStepVo stepVo = result.computeIfAbsent(step.getStepNo(), key -> {
|
||||
ApprovalFlowStepVo value = new ApprovalFlowStepVo();
|
||||
value.setStepNo(step.getStepNo());
|
||||
return value;
|
||||
});
|
||||
if (!StringUtils.hasText(stepVo.getStepName())) {
|
||||
stepVo.setStepName(step.getStepName());
|
||||
}
|
||||
if (!StringUtils.hasText(stepVo.getAssigneeType())) {
|
||||
stepVo.setAssigneeType(step.getAssigneeType());
|
||||
}
|
||||
if (stepVo.getAssigneeTargetId() == null) {
|
||||
stepVo.setAssigneeTargetId(step.getAssigneeTargetId());
|
||||
}
|
||||
if (!StringUtils.hasText(stepVo.getAssigneeTargetCode())) {
|
||||
stepVo.setAssigneeTargetCode(step.getAssigneeTargetCode());
|
||||
}
|
||||
if (!StringUtils.hasText(stepVo.getAssigneeTargetName())) {
|
||||
stepVo.setAssigneeTargetName(step.getAssigneeTargetName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user