关于审批方法,工作中不同的人有不同的写法,下面推荐一种我觉得算是比较优雅的一种写法,可以将业务逻辑和审批逻辑进行解耦,代码看起来也比较清爽。
一:请求参数
@Getter
@Setter
@ToString
@NoArgsConstructor
@Builder
public class ApprovalReq {
/** 业务数据主键 */
private String id;
/** 流程实例id */
private String processInstanceId;
/** 任务id */
private String taskId;
/** 审批结果 ApprovalResultEnum */
private String voteResult;
/** 下一节点key */
private String nextNode;
/** 下一处理人列表 */
private List<String> assigneeList;
/** 审批意见 */
private String suggestion;
}
二:审批结果枚举
public enum ApprovalResultEnum {
BACK("0", "退回"),
AGREE("1", "同意")
;
private String code;
private String name;
ApprovalResultEnum(String code, String name) {
this.code = code;
this.name = name;
}
}
三:通用审批模版方法
这里是一种简单的实现,可以根据自己的业务做一些适当调整和增强。
这里主要就是利用模版设计模式将审批逻辑确定下来,将不确定的业务逻辑分离出来。
@Slf4j
@Service
public class CommonApprovalService {
@Autowired
protected TaskService taskService;
@Autowired
protected HistoryService historyService;
/**
* 通用操作:无论审批结果是什么都会调用改方法(如业务检查,状态更新等操作)
*/
public void common(UserEntity user, ApprovalReq neq) {
}
/**
* 审批结果为“退回”时会调用该方法
*/
public void back(UserEntity user, ApprovalReq req) {
}
/**
* 审批结果为“同意”时会调用该方法
*/
public void agree(UserEntity user, ApprovalReq req) {
}
/**
* 流程审批结束后会调用该方法
*/
public void finished(UserEntity user, ApprovalReq req) {
}
/**
* 审批完成后发送邮件
*/
public void sendEmail(UserEntity user, ApprovalReq req) {
}
/**
* 模版审批方法
* @param user 当前登录的用户
* @param req 审批参数
*/
@Transactional(rollbackFor = Exception.class)
public void approval(UserEntity user, ApprovalReq req) {
Assert.isTrue(user != null && req != null, "参数校验失败");
log.info("AbstractApprovalServiceImpl#approval user={},req={}", user, req);
// 工作流常规检查
Task task = taskService.createTaskQuery().processInstanceId(req.getProcessInstanceId()).list().get(0);
this.checkProcessInstance(user, req.getProcessInstanceId(), req.getTaskId());
log.info("checkProcessInstance success");
//添加审批意见
this.addcomment(req.getProcessInstanceId(), task.getId(), user.getUserId(), req.getVoteResult(), req.getSuggestion());
log.info("addComment success");
// 公共操作
this.common(user, req);
log.info("common success");
if (Objects.equals(ApprovalResultEnum.BACK.getCode(), req.getVoteResult())) {
// 退回操作
log.info("back task: taskId={} taskDefKey={}", task.getId(), req.getNextNode());
// TODO 通过自定义命令实现
// actTaskManager.jumpTask(task.getId(), req.getNextNode());
Task nextTask = taskService.createTaskQuery().processInstanceId(req.getProcessInstanceId()).list().get(0);
taskService.setAssignee(nextTask.getId(), req.getAssigneeList().stream().collect(Collectors.joining(",")));
this.back(user, req);
log.info("back success user={}, req={}", user, req);
} else if (Objects.equals(ApprovalResultEnum.AGREE.getCode(), req.getVoteResult())) {
// 同意操作
log.info("agree task: taskId={} taskDefKey={}", task.getId(), req.getNextNode());
taskService.complete(task.getId());
log.info("completeUserTask success");
if (!CollectionUtils.isEmpty(req.getAssigneeList())) {
//设置下一任务负责人
Task nextTask = taskService.createTaskQuery().processInstanceId(req.getProcessInstanceId()).list().get(0);
taskService.setAssignee(nextTask.getId(), req.getAssigneeList().stream().collect(Collectors.joining(",")));
}
this.agree(user, req);
log.info("agree success user={}, req={}", user, req);
}
// 发送邮件(无论审批结果是什么都会发送邮件)
this.sendEmail(user, req);
// 审批结束操作
if (isFinished(req.getProcessInstanceId())) {
// 更新工作流完成时间
log.info("处理其他公共逻辑,如记录审批完成的时间等");
this.finished(user, req);
}
log.info("approval success user={}, req={}", user, req);
}
public boolean isFinished(String processInstanceId) {
// act_hi_procinst.END_TIME_
List<HistoricProcessInstance> hiProcInstList = historyService
.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceId)
.list();
if (CollectionUtils.isEmpty(hiProcInstList)) {
return false;
}
if (hiProcInstList.get(0).getEndTime() != null) {
return true;
}
return false;
}
public void addcomment(String processInstanceId, String taskId, String userId, String voteResult, String suggestion) {
Map<String, Object> commentMap = new HashMap<>();
commentMap.put("userId", userId);
commentMap.put("voteResult", voteResult);
commentMap.put("suggestion", suggestion);
String commentJSON = new JSONObject(commentMap).toJSONString();
taskService.addComment(taskId, processInstanceId, commentJSON);
log.info("addTaskComment success, processInstanceId={}, voteResult={}", processInstanceId, commentJSON);
}
public void checkProcessInstance(UserEntity user, String processInstanceId, String taskId) {
// 1.判断工作流是否完成
Assert.isTrue(this.isFinished(processInstanceId), "该流程已结束,请刷新重试!");
// 2. 检查当前节点是否已经审批过
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).list().get(0);
Assert.isTrue(Objects.equals(task.getId(), taskId), "当前审批节点已完成 !");
// 3. 检查当前登录用户是否为审批人
boolean isAssignee = false;
List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
for (Task item : taskList) {
String assignee = item.getAssignee();
if (!ObjectUtils.isEmpty(assignee)) {
List<String> assigneeList = Arrays.asList(assignee.split(","));
if (assigneeList.contains(user.getUserId())) {
isAssignee = true;
break;
}
}
}
Assert.isTrue(isAssignee, "当前用户没有权限审批!");
}
}
四:使用方法
自己的业务审批Service类来继承CommonApprovalService,在自己的业务类中只需要实现预留的方法即可。这样多个类型的审批对应多个Service,每个不同的Service不用具体关注审批相关的代码,只需要关注和审批相关的业务方法即可。这样业务代码和工作流代码解耦了。