Skip to content

Commit

Permalink
[Feature][admin] Task submit approval (#4070)
Browse files Browse the repository at this point in the history
  • Loading branch information
MactavishCui authored Jan 10, 2025
1 parent c968389 commit bbc4db4
Show file tree
Hide file tree
Showing 42 changed files with 2,351 additions and 56 deletions.
80 changes: 80 additions & 0 deletions dinky-admin/src/main/java/org/dinky/aop/TaskApprovalAspect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dinky.aop;

import org.dinky.data.annotations.CheckTaskApproval;
import org.dinky.data.enums.Status;
import org.dinky.data.exception.BusException;
import org.dinky.data.model.SystemConfiguration;
import org.dinky.utils.AspectUtil;

import java.lang.reflect.Method;
import java.util.Objects;

import javax.annotation.Resource;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Aspect
@Slf4j
@Component
public class TaskApprovalAspect {
@Resource
private ApplicationContext applicationContext;

/**
* Check whether the user has the permission to perform the task.
*
* @param joinPoint task operation
* @param checkTaskApproval check task approval aspect
* @return join point execute result
* @throws Throwable exception if task still need approval
*/
@Around(value = "@annotation(checkTaskApproval)")
public Object processAround(ProceedingJoinPoint joinPoint, CheckTaskApproval checkTaskApproval) throws Throwable {
if (SystemConfiguration.getInstances().enableTaskSubmitApprove()) {
Class checkParam = checkTaskApproval.checkParam();
Object param = AspectUtil.getParam(joinPoint, checkParam);
if (Objects.nonNull(param)) {
Object bean = applicationContext.getBean(checkTaskApproval.checkInterface());
Class<?> clazz = bean.getClass();
Method method = clazz.getMethod(checkTaskApproval.checkMethod(), param.getClass());
Object invoke = method.invoke(bean, param);
if (invoke != null && (Boolean) invoke) {
throw new BusException(Status.SYS_APPROVAL_TASK_NOT_APPROVED);
}
}
}

Object result;
try {
result = joinPoint.proceed();
} catch (Throwable e) {
throw e;
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@
import org.dinky.data.enums.TaskOwnerLockStrategyEnum;
import org.dinky.data.exception.BusException;
import org.dinky.data.model.SystemConfiguration;
import org.dinky.utils.AspectUtil;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;

Expand All @@ -36,7 +35,6 @@
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.ApplicationContext;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -65,7 +63,7 @@ public Object processAround(ProceedingJoinPoint joinPoint, CheckTaskOwner checkT
SystemConfiguration.getInstances().GetTaskOwnerLockStrategyValue())
&& BaseConstant.ADMIN_ID != StpUtil.getLoginIdAsInt()) {
Class checkParam = checkTaskOwner.checkParam();
Object param = getParam(joinPoint, checkParam);
Object param = AspectUtil.getParam(joinPoint, checkParam);
if (Objects.nonNull(param)) {
Object bean = applicationContext.getBean(checkTaskOwner.checkInterface());
Class<?> clazz = bean.getClass();
Expand All @@ -85,42 +83,4 @@ public Object processAround(ProceedingJoinPoint joinPoint, CheckTaskOwner checkT
}
return result;
}

private Object getParam(ProceedingJoinPoint joinPoint, Class paramAnno) throws IllegalAccessException {
Object[] params = joinPoint.getArgs();
if (params.length == 0) {
return null;
}

Object paramObj = null;
// Get the method, here you can convert the signature strong to MethodSignature
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();

Annotation[][] annotations = method.getParameterAnnotations();
for (int i = 0; i < annotations.length; i++) {
Object param = params[i];
if (param == null) {
continue;
}
Annotation[] paramAnn = annotations[i];
for (Annotation annotation : paramAnn) {
if (annotation.annotationType() == paramAnno) {
paramObj = param;
break;
}
}
if (paramObj == null) {
Field[] fields = param.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(paramAnno)) {
field.setAccessible(true);
paramObj = field.get(param);
break;
}
}
}
}
return paramObj;
}
}
3 changes: 2 additions & 1 deletion dinky-admin/src/main/java/org/dinky/configure/AppConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public void addInterceptors(InterceptorRegistry registry) {
.addPathPatterns("/api/role/**")
.addPathPatterns("/api/fragment/**")
.addPathPatterns("/api/git/**")
.addPathPatterns("/api/jar/*");
.addPathPatterns("/api/jar/*")
.addPathPatterns("/api/approval/*");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public class MybatisPlusConfig {
"dinky_task",
"dinky_task_statement",
"dinky_git_project",
"dinky_task_version");
"dinky_task_version",
"dinky_approval");

@Bean
@Profile("postgresql")
Expand Down
120 changes: 120 additions & 0 deletions dinky-admin/src/main/java/org/dinky/controller/ApprovalController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dinky.controller;

import org.dinky.data.annotations.CheckTaskOwner;
import org.dinky.data.annotations.ProcessId;
import org.dinky.data.annotations.TaskId;
import org.dinky.data.dto.ApprovalDTO;
import org.dinky.data.enums.ApprovalEvent;
import org.dinky.data.model.Approval;
import org.dinky.data.model.rbac.User;
import org.dinky.data.result.ProTableResult;
import org.dinky.data.result.Result;
import org.dinky.service.ApprovalService;
import org.dinky.service.TaskService;

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.JsonNode;

import cn.dev33.satoken.annotation.SaCheckLogin;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestController
@Slf4j
@Api(tags = "Approval Controller", hidden = true)
@RequestMapping("/api/approval")
@SaCheckLogin
@RequiredArgsConstructor
public class ApprovalController {

private final ApprovalService approvalService;

@PostMapping("/getSubmittedApproval")
@ApiOperation("Get all approvals submitted by current user")
ProTableResult<Approval> getSubmittedApproval(@RequestBody JsonNode para) {
return approvalService.getSubmittedApproval(para);
}

@PostMapping("/getApprovalToBeReviewed")
@ApiOperation("Get all approvals current user is required for review")
ProTableResult<Approval> getApprovalToBeReviewed(@RequestBody JsonNode para) {
return approvalService.getApprovalToBeReviewed(para);
}

@CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class)
@PutMapping("/createTaskApproval")
Result<Approval> createTaskApproval(@TaskId @ProcessId @RequestParam Integer taskId) {
return Result.succeed(approvalService.createTaskApproval(taskId));
}

@PostMapping("/submit")
@ApiOperation("Submit approval")
Result<Void> submit(@RequestBody ApprovalDTO approvalDTO) {
approvalService.handleApproveEvent(ApprovalEvent.SUBMIT, approvalDTO);
return Result.succeed();
}

@PostMapping("/withdraw")
@ApiOperation("Withdraw approval")
Result<Void> withdraw(@RequestBody ApprovalDTO approvalDTO) {
approvalService.handleApproveEvent(ApprovalEvent.WITHDRAW, approvalDTO);
return Result.succeed();
}

@PostMapping("/approve")
@ApiOperation("Approve approval")
Result<Void> approve(@RequestBody ApprovalDTO approvalDTO) {
approvalService.handleApproveEvent(ApprovalEvent.APPROVE, approvalDTO);
return Result.succeed();
}

@PostMapping("/reject")
@ApiOperation("Reject approval")
Result<Void> reject(@RequestBody ApprovalDTO approvalDTO) {
approvalService.handleApproveEvent(ApprovalEvent.REJECT, approvalDTO);
return Result.succeed();
}

@PostMapping("/cancel")
@ApiOperation("Reject approval")
Result<Void> cancel(@RequestBody ApprovalDTO approvalDTO) {
approvalService.handleApproveEvent(ApprovalEvent.CANCEL, approvalDTO);
return Result.succeed();
}

@GetMapping("/getReviewers")
@ApiOperation("Get reviewers that from current tenant")
Result<List<User>> getReviewers(@RequestParam Integer tenantId) {
return Result.succeed(approvalService.getTaskReviewerList(tenantId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.dinky.controller;

import org.dinky.data.annotations.CheckTaskApproval;
import org.dinky.data.annotations.CheckTaskOwner;
import org.dinky.data.annotations.ExecuteProcess;
import org.dinky.data.annotations.Log;
Expand All @@ -44,6 +45,7 @@
import org.dinky.gateway.result.SavePointResult;
import org.dinky.job.JobResult;
import org.dinky.mybatis.annotation.Save;
import org.dinky.service.ApprovalService;
import org.dinky.service.TaskService;
import org.dinky.trans.ExecuteJarParseStrategyUtil;
import org.dinky.utils.SqlUtil;
Expand Down Expand Up @@ -97,6 +99,7 @@ public class TaskController {
@ApiOperation("Submit Task")
@Log(title = "Submit Task", businessType = BusinessType.SUBMIT)
@ExecuteProcess(type = ProcessType.FLINK_SUBMIT)
@CheckTaskApproval(checkParam = TaskId.class, checkInterface = ApprovalService.class)
@CheckTaskOwner(checkParam = TaskId.class, checkInterface = TaskService.class)
public Result<JobResult> submitTask(@TaskId @ProcessId @RequestParam Integer id) throws Exception {
JobResult jobResult =
Expand Down
50 changes: 50 additions & 0 deletions dinky-admin/src/main/java/org/dinky/data/dto/ApprovalDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.dinky.data.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@ApiModel(value = "ApprovalDTO", description = "Approval Data Transfer Object")
public class ApprovalDTO {
@ApiModelProperty(value = "Approval ID", dataType = "Integer", example = "123", notes = "The ID of the approval")
private Integer id;

@ApiModelProperty(value = "Task ID", dataType = "Integer", example = "123", notes = "The ID of the task")
private Integer taskId;

@ApiModelProperty(
value = "Reviewer id required for current approval",
dataType = "Integer",
example = "123",
notes = "The ID of the reviewer")
private Integer reviewer;

@ApiModelProperty(
value = "Comment",
dataType = "String",
example = "Looks good to me/Please take a look",
notes = "Comment from reviewer or submitter")
private String comment;
}
Loading

0 comments on commit bbc4db4

Please sign in to comment.