From 935db3fe59c531e807989ab1001c06d4aa719b76 Mon Sep 17 00:00:00 2001 From: wangyu <727842003@qq.com> Date: Fri, 10 Dec 2021 21:29:56 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E6=8F=90=E4=BA=A4=E5=AE=A1?= =?UTF-8?q?=E6=89=B9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../approval/auditor/ApproveAuditor.java | 31 ++++ .../controller/ApprovalController.java | 88 ++++++++++ .../controller/ApproveRecordController.java | 63 +------ .../approval/domain/ApprovalDomain.java | 2 +- .../domain/submit/ApprovalSubmitDto.java | 28 +++ .../approval/domain/todo/ApprovalDto.java | 25 +++ .../approval/enums/ApproveStatus.java | 3 +- .../service/ModuleDelegateService.java | 165 +++++++++++++++++- .../framework/beans/meta/BeanController.java | 1 + 9 files changed, 339 insertions(+), 67 deletions(-) create mode 100644 flyfish-approval/src/main/java/com/flyfish/framework/approval/auditor/ApproveAuditor.java create mode 100644 flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApprovalController.java create mode 100644 flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/submit/ApprovalSubmitDto.java create mode 100644 flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/todo/ApprovalDto.java diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/auditor/ApproveAuditor.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/auditor/ApproveAuditor.java new file mode 100644 index 0000000..6a29f61 --- /dev/null +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/auditor/ApproveAuditor.java @@ -0,0 +1,31 @@ +package com.flyfish.framework.approval.auditor; + +import com.flyfish.framework.approval.domain.ApprovalDomain; +import com.flyfish.framework.approval.enums.ApproveStatus; +import com.flyfish.framework.auditor.ReactiveBeanAuditor; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +/** + * 审批拦截审计器 + * 在用户首次提交审核 + * + * @author wangyu + */ +@Component +public class ApproveAuditor implements ReactiveBeanAuditor { + + /** + * 对实体进行审查,并补全相关字段 + * + * @param data 原数据 + * @return 结果 + */ + @Override + public Mono audit(ApprovalDomain data) { + if (null == data.getApproveStatus()) { + data.setApproveStatus(ApproveStatus.DRAFT); + } + return Mono.just(data); + } +} diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApprovalController.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApprovalController.java new file mode 100644 index 0000000..60eff4b --- /dev/null +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApprovalController.java @@ -0,0 +1,88 @@ +package com.flyfish.framework.approval.controller; + +import com.flyfish.framework.approval.domain.ApprovalDomain; +import com.flyfish.framework.approval.domain.record.ApproveRecord; +import com.flyfish.framework.approval.domain.record.ApproveRecordListVo; +import com.flyfish.framework.approval.domain.record.ApproveRecordQo; +import com.flyfish.framework.approval.domain.submit.ApprovalSubmitDto; +import com.flyfish.framework.approval.domain.todo.ApprovalDto; +import com.flyfish.framework.approval.domain.todo.ApprovalListVo; +import com.flyfish.framework.approval.service.ApproveRecordService; +import com.flyfish.framework.approval.service.ModuleDelegateService; +import com.flyfish.framework.bean.Result; +import com.flyfish.framework.configuration.annotations.CurrentUser; +import com.flyfish.framework.configuration.annotations.PagedQuery; +import com.flyfish.framework.domain.base.Vo; +import com.flyfish.framework.domain.po.User; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Mono; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; + +/** + * 审批专用controller + * + * @author wangyu + */ +@RestController +@RequestMapping("approves") +public class ApprovalController { + + @Resource + private ApproveRecordService approveRecordService; + @Resource + private ModuleDelegateService moduleDelegateService; + + /** + * 查询视图 + * + * @return 结果 + */ + @GetMapping("records") + public Mono>>> views(@PagedQuery ApproveRecordQo qo) { + return approveRecordService.getPageList(qo) + .map(Result::accept) + .map(result -> result.mapList((ApproveRecord item) -> new ApproveRecordListVo().from(item))); + } + + /** + * 查询待办列表 + * + * @param qo 查询实体 + * @param user 用户 + * @return 结果 + */ + @GetMapping("todos") + public Mono>>> todos(@PagedQuery ApproveRecordQo qo, @CurrentUser User user) { + qo.setApprover(user.getId()); + qo.setApproved(false); + return approveRecordService.getPageList(qo) + .map(Result::accept) + .map(result -> result.mapList((ApproveRecord item) -> new ApprovalListVo().from(item))); + } + + /** + * 提交审批 + * 需要指定提交候选人,按顺序审批 + * + * @return 结果 + */ + @PostMapping("submit") + public Mono> submit(@Valid @RequestBody ApprovalSubmitDto body) { + return moduleDelegateService.submit(body).map(Result::ok); + } + + + /** + * 审批数据 + * + * @param approval 数据 + * @return 结果 + */ + @PostMapping("approval") + public Mono> approval(@RequestBody ApprovalDto approval) { + return moduleDelegateService.approval(approval).map(Result::ok); + } +} diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApproveRecordController.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApproveRecordController.java index 0e3b4ff..17cc0d1 100644 --- a/flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApproveRecordController.java +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/controller/ApproveRecordController.java @@ -1,22 +1,10 @@ package com.flyfish.framework.approval.controller; -import com.flyfish.framework.approval.domain.ApprovalDomain; import com.flyfish.framework.approval.domain.record.ApproveRecord; -import com.flyfish.framework.approval.domain.record.ApproveRecordListVo; import com.flyfish.framework.approval.domain.record.ApproveRecordQo; -import com.flyfish.framework.approval.domain.todo.ApprovalListVo; -import com.flyfish.framework.approval.service.ModuleDelegateService; -import com.flyfish.framework.bean.Result; -import com.flyfish.framework.configuration.annotations.CurrentUser; -import com.flyfish.framework.configuration.annotations.PagedQuery; import com.flyfish.framework.controller.reactive.ReactiveBaseController; -import com.flyfish.framework.domain.base.Vo; -import com.flyfish.framework.domain.po.User; -import org.springframework.web.bind.annotation.*; -import reactor.core.publisher.Mono; - -import javax.annotation.Resource; -import java.util.List; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; /** * 审批记录控制器 @@ -24,52 +12,7 @@ import java.util.List; * @author wangyu */ @RestController -@RequestMapping("approves") +@RequestMapping("approve-records") public class ApproveRecordController extends ReactiveBaseController { - @Resource - private ModuleDelegateService moduleDelegateService; - - /** - * 查询视图 - * - * @return 结果 - */ - @GetMapping("records") - public Mono>>> views(@PagedQuery ApproveRecordQo qo) { - return reactiveService.getPageList(qo) - .map(Result::accept) - .map(result -> result.mapList((ApproveRecord item) -> new ApproveRecordListVo().from(item))); - } - - /** - * 查询待办列表 - * - * @param qo 查询实体 - * @param user 用户 - * @return 结果 - */ - @GetMapping("todos") - public Mono>>> todos(@PagedQuery ApproveRecordQo qo, @CurrentUser User user) { - qo.setApprover(user.getId()); - qo.setApproved(false); - return reactiveService.getPageList(qo) - .map(Result::accept) - .map(result -> result.mapList((ApproveRecord item) -> new ApprovalListVo().from(item))); - } - - /** - * 通用审批 - * - * @param module 模块 - * @param dataId 数据id - * @return 结果 - */ - @PostMapping("approval/{module}/{dataId}") - public Mono> approval(@PathVariable String module, @PathVariable String dataId, - @RequestBody ApproveRecord record) { - record.setModule(module); - record.setDataId(dataId); - return moduleDelegateService.approval(record).map(Result::ok); - } } diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/ApprovalDomain.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/ApprovalDomain.java index 47d3a5f..57ae948 100644 --- a/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/ApprovalDomain.java +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/ApprovalDomain.java @@ -23,5 +23,5 @@ public abstract class ApprovalDomain extends AuthorizedDomain { private List approvers; // 下个审批人 - private String nextApprover; + private Integer next; } diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/submit/ApprovalSubmitDto.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/submit/ApprovalSubmitDto.java new file mode 100644 index 0000000..6629fa8 --- /dev/null +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/submit/ApprovalSubmitDto.java @@ -0,0 +1,28 @@ +package com.flyfish.framework.approval.domain.submit; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * 审批提交数据 + * + * @author wangyu + */ +@Data +public class ApprovalSubmitDto { + + // 数据主键 + @NotBlank(message = "数据主键不可为空!") + private String id; + + // 数据模块 + @NotBlank(message = "数据模块不可为空!") + private String module; + + // 待审批人列表 + @NotEmpty(message = "待审批人不可为空!") + private List approvers; +} diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/todo/ApprovalDto.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/todo/ApprovalDto.java new file mode 100644 index 0000000..e6d5a3e --- /dev/null +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/domain/todo/ApprovalDto.java @@ -0,0 +1,25 @@ +package com.flyfish.framework.approval.domain.todo; + +import com.flyfish.framework.approval.enums.ApproveAction; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 审批dto + * + * @author wangyu + */ +@Data +public class ApprovalDto { + + @NotBlank(message = "审批记录id不可为空!") + private String id; + + @NotNull(message = "审批操作不可为空!") + private ApproveAction action; + + @NotNull(message = "审批意见不可为空!") + private String opinion; +} diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/enums/ApproveStatus.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/enums/ApproveStatus.java index 48d8958..082ca32 100644 --- a/flyfish-approval/src/main/java/com/flyfish/framework/approval/enums/ApproveStatus.java +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/enums/ApproveStatus.java @@ -6,13 +6,14 @@ import lombok.Getter; /** * 审批状态 + * * @author wangyu */ @Getter @AllArgsConstructor public enum ApproveStatus implements NamedEnum { - PENDING("待审批"), APPROVING("审批中"), APPROVED("已审批"), REJECTED("已拒绝"); + DRAFT("草稿"), PENDING("待审批"), APPROVING("审批中"), APPROVED("已审批"), REJECTED("已拒绝"); private final String name; } diff --git a/flyfish-approval/src/main/java/com/flyfish/framework/approval/service/ModuleDelegateService.java b/flyfish-approval/src/main/java/com/flyfish/framework/approval/service/ModuleDelegateService.java index 8c9b322..8e79d32 100644 --- a/flyfish-approval/src/main/java/com/flyfish/framework/approval/service/ModuleDelegateService.java +++ b/flyfish-approval/src/main/java/com/flyfish/framework/approval/service/ModuleDelegateService.java @@ -2,14 +2,24 @@ package com.flyfish.framework.approval.service; import com.flyfish.framework.approval.domain.ApprovalDomain; import com.flyfish.framework.approval.domain.record.ApproveRecord; +import com.flyfish.framework.approval.domain.submit.ApprovalSubmitDto; +import com.flyfish.framework.approval.domain.todo.ApprovalDto; +import com.flyfish.framework.approval.enums.ApproveAction; +import com.flyfish.framework.approval.enums.ApproveStatus; +import com.flyfish.framework.beans.meta.RestBean; import com.flyfish.framework.domain.base.DomainService; +import com.flyfish.framework.exception.biz.InvalidBusinessException; import com.flyfish.framework.service.BaseReactiveService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Mono; +import javax.annotation.Resource; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -21,6 +31,9 @@ import java.util.stream.Collectors; @Service public class ModuleDelegateService { + @Resource + private ApproveRecordService approveRecordService; + // 审批的服务们 private Map> approvalServices; @@ -41,12 +54,154 @@ public class ModuleDelegateService { } /** - * 提交审批 - * @param record 记录 + * 审批数据 + * + * @param approval 记录 * @return 结果 */ - public Mono approval(ApproveRecord record) { - getService(record.getModule()).getById(record.getDataId()) - .map(domain -> domain) + @Transactional(rollbackFor = Exception.class) + public Mono approval(ApprovalDto approval) { + return approveRecordService.getById(approval.getId()) + // 保存记录 + .flatMap(record -> { + if (record.getApproved()) { + return Mono.error(new InvalidBusinessException("该审批已被处理,请勿重复操作!")); + } + record.setApproved(true); + record.setAction(approval.getAction()); + record.setOpinion(approval.getOpinion()); + return approveRecordService.updateById(record); + }) + // 修改主记录 + .flatMap(record -> { + BaseReactiveService service = getService(record.getModule()); + return service.getById(record.getDataId()) + .flatMap(domain -> { + // 如果已经审批过,直接返回 + if (domain.getApproveStatus() == ApproveStatus.APPROVED) { + return Mono.error(new InvalidBusinessException("该审批已被处理,请勿重复操作!")); + } + // 尚未提交审批,不处理 + if (domain.getApproveStatus() == ApproveStatus.DRAFT || domain.getApproveStatus() == ApproveStatus.REJECTED) { + return Mono.error(new InvalidBusinessException("该数据尚未提交审批,请确认后再操作!")); + } + // 终止标记 + boolean end = false; + // 审批人 + String approver = null; + // 尝试流转 + switch (record.getAction()) { + case AGREE: + String next = determineNext(domain); + // 下一个为空,代表审批完了,改状态 + if (null == next) { + domain.setApproveStatus(ApproveStatus.APPROVED); + domain.setNext(-1); + end = true; + } else { + domain.setApproveStatus(ApproveStatus.APPROVING); + domain.setNext(domain.getNext() + 1); + approver = next; + } + break; + case REJECT: + // 拒绝,如果已经是第一步了,设置拒绝状态 + if (domain.getNext() == 0) { + domain.setApproveStatus(ApproveStatus.REJECTED); + end = true; + } else { + domain.setNext(domain.getNext() - 1); + approver = domain.getApprovers().get(domain.getNext()); + } + break; + case REJECT_ALL: + // 直接打回拒绝状态,需要重新修改提交 + domain.setNext(0); + domain.setApproveStatus(ApproveStatus.REJECTED); + end = true; + break; + } + // 没结束 + if (!end) { + // 构建下一步操作 + ApproveRecord nextRecord = new ApproveRecord(); + nextRecord.setModule(record.getModule()); + nextRecord.setDataId(record.getId()); + nextRecord.setApproved(false); + nextRecord.setModuleName(moduleName(domain.getClass())); + nextRecord.setName(operation(approval.getAction())); + nextRecord.setApprover(approver); + return approveRecordService.create(nextRecord) + .flatMap(r -> service.updateById(domain)); + } + return service.updateById(domain); + }); + }); + + } + + /** + * 提交审核 + * + * @param data 数据 + * @return 结果 + */ + public Mono submit(ApprovalSubmitDto data) { + BaseReactiveService service = getService(data.getModule()); + return service.getById(data.getId()) + .flatMap(domain -> { + domain.setApprovers(data.getApprovers()); + domain.setApproveStatus(ApproveStatus.PENDING); + domain.setNext(0); + return service.updateById(domain); + }) + .flatMap(domain -> { + ApproveRecord record = new ApproveRecord(); + record.setName("提交审核"); + record.setModule(data.getModule()); + record.setDataId(data.getId()); + record.setApproved(false); + record.setApprover(domain.getApprovers().get(domain.getNext())); + record.setModuleName(moduleName(domain.getClass())); + return approveRecordService.create(record).thenReturn(domain); + }); + } + + /** + * 获取模块名称 + * + * @param clazz 类 + * @return 结果 + */ + private String moduleName(Class clazz) { + return Optional.ofNullable(AnnotationUtils.findAnnotation(clazz, RestBean.class)) + .map(RestBean::name).orElse("未知模块"); + } + + /** + * 决定下一位审批人 + * + * @param domain 审批实体 + * @return 结果 + */ + private String determineNext(ApprovalDomain domain) { + // 获取当前位置 + int index = domain.getNext(); + int next = index + 1; + // 下一个位置 + if (next < domain.getApprovers().size()) { + return domain.getApprovers().get(next); + } + return null; + } + + /** + * 操作中文名 + * + * @param action 操作 + * @return 结果 + */ + private String operation(ApproveAction action) { + return "将审批进行了\"" + action.getName() + "\"操作"; } } diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java index 1c2e279..a1f60c1 100644 --- a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java +++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java @@ -74,6 +74,7 @@ public class BeanController { BeanInfo info = new BeanInfo(); if (clazz.isAnnotationPresent(RestBean.class)) { RestBean annotation = clazz.getAnnotation(RestBean.class); + info.setType(clazz.getSuperclass().getSimpleName()); info.setName(annotation.name()); info.setCode(annotation.value()); info.setSearch(BeanProperty.from(annotation.queryClass()));