Procházet zdrojové kódy

通用事项流程审批接口、查询接口、导出接口

dongpo před 1 rokem
rodič
revize
8fe0ddaeb6
12 změnil soubory, kde provedl 485 přidání a 352 odebrání
  1. 2 2
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/DictDataConstants.java
  2. 1 0
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
  3. 59 41
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/universal/OaUniversalController.java
  4. 32 20
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/universal/vo/OaUniversalRespVO.java
  5. 0 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/universal/vo/OaUniversalSaveReqVO.java
  6. 2 2
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java
  7. 23 26
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/universal/OaUniversalService.java
  8. 162 87
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/universal/OaUniversalServiceImpl.java
  9. 8 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
  10. 21 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
  11. 171 171
      yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/oa/universal/OaUniversalServiceImplTest.java
  12. 4 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/DictDataConstants.java

@@ -14,7 +14,7 @@ public interface DictDataConstants {
     /**
      * 已提交状态
      */
-    String OA_AUDIT_STATUS_SUBMITTED = "1";
+    String OA_AUDIT_STATUS_COMMITTED = "1";
     /**
      * 审核中状态
      */
@@ -30,7 +30,7 @@ public interface DictDataConstants {
     /**
      * 已驳回状态
      */
-    String OA_AUDIT_STATUS_REJECTED = "5";
+    String OA_AUDIT_STATUS_RETURNED = "5";
     /**
      * 已撤回状态
      */

+ 1 - 0
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java

@@ -52,6 +52,7 @@ public interface ErrorCodeConstants {
     ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务");
     ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人");
     ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
+    ErrorCode TASK_REVOCATION_NO_ALLOWED = new ErrorCode(1_009_006_015, "操作失败,原因:只有已提交状态的流程才能由申请人撤回!");
     ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
 
     // ========== 动态表单模块 1-009-010-000 ==========

+ 59 - 41
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/universal/OaUniversalController.java

@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollectionUtil;
 import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
@@ -13,7 +12,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.oa.universal.vo.OaUniversalR
 import cn.iocoder.yudao.module.bpm.controller.admin.oa.universal.vo.OaUniversalSaveReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskReturnReqVO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.universal.OaUniversalDO;
 import cn.iocoder.yudao.module.bpm.service.oa.universal.OaUniversalService;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import io.swagger.v3.oas.annotations.Operation;
@@ -31,7 +29,6 @@ import java.util.List;
 
 import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
 
 @Tag(name = "管理后台 - 通用事项审批流程信息")
 @RestController
@@ -46,19 +43,19 @@ public class OaUniversalController {
     @Operation(summary = "提交通用事项审批流程信息")
     @ApiOperationSupport(order = 1)
     @PreAuthorize("@ss.hasPermission('bpm:oa-universal:commit')")
-    public CommonResult<Long> commitOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO createReqVO) {
-        if (CollectionUtil.isEmpty(createReqVO.getStartUserSelectAssignees())) {
+    public CommonResult<Long> commitOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO commitReqVO) {
+        if (CollectionUtil.isEmpty(commitReqVO.getStartUserSelectAssignees())) {
             return CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(),"请选择审批人");
         }
-        Long oaUniversalId = oaUniversalService.commitOaUniversal(createReqVO);
+        Long oaUniversalId = oaUniversalService.commitOaUniversal(commitReqVO);
         return success(oaUniversalId, "提交成功");
     }
     @PostMapping("/staging")
     @Operation(summary = "暂存通用事项审批流程信息")
     @ApiOperationSupport(order = 2)
     @PreAuthorize("@ss.hasPermission('bpm:oa-universal:staging')")
-    public CommonResult<Long> startOaUniversal(@RequestBody OaUniversalSaveReqVO createReqVO) {
-        Long oaUniversalId = oaUniversalService.stagingOaUniversal(createReqVO);
+    public CommonResult<Long> startOaUniversal(@RequestBody OaUniversalSaveReqVO stagingReqVO) {
+        Long oaUniversalId = oaUniversalService.stagingOaUniversal(stagingReqVO);
         return success(oaUniversalId, "暂存成功");
     }
 
@@ -66,8 +63,8 @@ public class OaUniversalController {
     @Operation(summary = "审批同意通用事项审批流程信息")
     @ApiOperationSupport(order = 3)
     @PreAuthorize("@ss.hasPermission('bpm:oa-universal:agree')")
-    public CommonResult<Long> agreeOaUniversal(@Valid @RequestBody BpmTaskApproveReqVO approveReqVO) {
-        Long result = oaUniversalService.agreeOaUniversal(approveReqVO);
+    public CommonResult<Long> agreeOaUniversal(@Valid @RequestBody BpmTaskApproveReqVO agreeReqVO) {
+        Long result = oaUniversalService.agreeOaUniversal(agreeReqVO);
         return success(result, "审批成功");
     }
 
@@ -75,8 +72,8 @@ public class OaUniversalController {
     @Operation(summary = "驳回通用事项审批流程信息")
     @ApiOperationSupport(order = 4)
     @PreAuthorize("@ss.hasPermission('bpm:oa-universal:disagree')")
-    public CommonResult<Long> disagreeOaUniversal(@Valid @RequestBody BpmTaskReturnReqVO returnReqVO) {
-        Long result = oaUniversalService.disagreeOaUniversal(returnReqVO);
+    public CommonResult<Long> disagreeOaUniversal(@Valid @RequestBody BpmTaskReturnReqVO disagreeReqVO) {
+        Long result = oaUniversalService.disagreeOaUniversal(disagreeReqVO);
         return success(result, "驳回成功");
     }
 
@@ -84,68 +81,89 @@ public class OaUniversalController {
     @Operation(summary = "撤回通用事项审批流程信息")
     @ApiOperationSupport(order = 5)
     @PreAuthorize("@ss.hasPermission('bpm:oa-universal:revocation')")
-    public CommonResult<Long> revocationOaUniversal(@Valid @RequestBody BpmTaskApproveReqVO approveReqVO) {
-        Long result = oaUniversalService.revocationOaUniversal(approveReqVO);
+    public CommonResult<Long> revocationOaUniversal(@Valid @RequestBody BpmTaskApproveReqVO revocationReqVO) {
+        Long result = oaUniversalService.revocationOaUniversal(revocationReqVO);
         return success(result, "撤回成功");
     }
 
-    @PostMapping("/updateComplete")
-    @Operation(summary = "驳回和撤回后再次提交或关闭通用事项审批流程信息")
-    @PreAuthorize("@ss.hasPermission('bpm:oa-entry:update-complete')")
-    public CommonResult<Long> updateCompleteOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO createReqVO) {
-        return success(oaUniversalService.updateCompleteOaUniversal(getLoginUserId(), createReqVO, createReqVO.getBpmTaskApproveReqVO()));
+    @PostMapping("/reCommit")
+    @Operation(summary = "驳回或撤回后再次提交通用用事项审批流程信息")
+    @ApiOperationSupport(order = 6)
+    @PreAuthorize("@ss.hasPermission('bpm:oa-universal:re-commit')")
+    public CommonResult<Long> reCommitOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO reCommitReqVO) {
+        if (reCommitReqVO.getId() == null) {
+            return CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "id不能为空");
+        }
+        Long result = oaUniversalService.reCommitOaUniversal(reCommitReqVO);
+        return success(result, "提交成功");
     }
 
+    @PostMapping("/close")
+    @Operation(summary = "驳回或撤回后关闭通用事项审批流程信息")
+    @ApiOperationSupport(order = 7)
+    @PreAuthorize("@ss.hasPermission('bpm:oa-entry:close')")
+    public CommonResult<Long> updateCompleteOaUniversal(@RequestBody OaUniversalSaveReqVO closeReqVO) {
+        if (closeReqVO.getId() == null) {
+            return CommonResult.error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "id不能为空");
+        }
+        Long result = oaUniversalService.closeOaUniversal(closeReqVO);
 
-    @PostMapping("/create")
-    @Operation(summary = "创建通用事项审批流程信息")
-    @PreAuthorize("@ss.hasPermission('bpm:oa-universal:create')")
-    public CommonResult<Long> createOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO createReqVO) {
-        return success(oaUniversalService.createOaUniversal(createReqVO));
+        return success(result, "关闭成功");
     }
 
-    @PutMapping("/update")
-    @Operation(summary = "更新通用事项审批流程信息")
-    @PreAuthorize("@ss.hasPermission('bpm:oa-universal:update')")
-    public CommonResult<Boolean> updateOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO updateReqVO) {
-        oaUniversalService.updateOaUniversal(updateReqVO);
-        return success(true);
-    }
+
+    // @PostMapping("/create")
+    // @Operation(summary = "创建通用事项审批流程信息")
+    // @PreAuthorize("@ss.hasPermission('bpm:oa-universal:create')")
+    // public CommonResult<Long> createOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO createReqVO) {
+    //     return success(oaUniversalService.createOaUniversal(createReqVO));
+    // }
+    //
+    // @PutMapping("/update")
+    // @Operation(summary = "更新通用事项审批流程信息")
+    // @PreAuthorize("@ss.hasPermission('bpm:oa-universal:update')")
+    // public CommonResult<Boolean> updateOaUniversal(@Valid @RequestBody OaUniversalSaveReqVO updateReqVO) {
+    //     oaUniversalService.updateOaUniversal(updateReqVO);
+    //     return success(true);
+    // }
 
     @DeleteMapping("/delete")
     @Operation(summary = "删除通用事项审批流程信息")
+    @ApiOperationSupport(order = 8)
     @Parameter(name = "id", description = "编号", required = true)
     @PreAuthorize("@ss.hasPermission('bpm:oa-universal:delete')")
     public CommonResult<Boolean> deleteOaUniversal(@RequestParam("id") Long id) {
         oaUniversalService.deleteOaUniversal(id);
-        return success(true);
+        return success(true, "删除成功");
     }
 
     @GetMapping("/get")
-    @Operation(summary = "获得通用事项审批流程信息")
-    @Parameter(name = "id", description = "编号", required = true, example = "1024")
-    @PreAuthorize("@ss.hasPermission('bpm:oa-universal:query')")
+    @Operation(summary = "根据id获得通用事项审批流程信息")
+    @Parameter(name = "id", description = "编号", required = true, example = "1")
+    @ApiOperationSupport(order = 9)
+    @PreAuthorize("@ss.hasPermission('bpm:oa-universal:query-id')")
     public CommonResult<OaUniversalRespVO> getOaUniversal(@RequestParam("id") Long id) {
         OaUniversalRespVO respVO = oaUniversalService.getOaUniversal(id);
-        return success(respVO);
+        return success(respVO, "查询对象成功");
     }
 
     @GetMapping("/page")
     @Operation(summary = "获得通用事项审批流程信息分页")
-    @PreAuthorize("@ss.hasPermission('bpm:oa-universal:query')")
+    @ApiOperationSupport(order = 10)
+    @PreAuthorize("@ss.hasPermission('bpm:oa-universal:query-page')")
     public CommonResult<PageResult<OaUniversalRespVO>> getOaUniversalPage(@Valid OaUniversalPageReqVO pageReqVO) {
-        PageResult<OaUniversalDO> pageResult = oaUniversalService.getOaUniversalPage(pageReqVO);
-        return success(BeanUtils.toBean(pageResult, OaUniversalRespVO.class));
+        return success(oaUniversalService.getOaUniversalPage(pageReqVO), "查询列表成功");
     }
 
     @GetMapping("/export-excel")
     @Operation(summary = "导出通用事项审批流程信息 Excel")
+    @ApiOperationSupport(order = 11)
     @PreAuthorize("@ss.hasPermission('bpm:oa-universal:export')")
     @ApiAccessLog(operateType = EXPORT)
     public void exportOaUniversalExcel(@Valid OaUniversalPageReqVO pageReqVO,
               HttpServletResponse response) throws IOException {
-        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
-        List<OaUniversalDO> list = oaUniversalService.getOaUniversalPage(pageReqVO).getList();
+        PageResult<OaUniversalRespVO> pageResult = oaUniversalService.exportOaUniversalExcel(pageReqVO);
+        List<OaUniversalRespVO> list = pageResult.getList();
         // 导出 Excel
         ExcelUtils.write(response, "通用事项审批流程信息.xls", "数据", OaUniversalRespVO.class,
                         BeanUtils.toBean(list, OaUniversalRespVO.class));

+ 32 - 20
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/universal/vo/OaUniversalRespVO.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.controller.admin.oa.universal.vo;
 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
 import cn.iocoder.yudao.module.infra.api.file.dto.FileDTO;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
 import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -17,48 +18,52 @@ import java.util.List;
 @ExcelIgnoreUnannotated
 public class OaUniversalRespVO {
 
-    @Schema(description = "自增主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @Schema(description = "自增主键")
     @ExcelProperty("自增主键")
     private Long id;
 
-    @Schema(description = "uuid", example = "12260")
+    @Schema(description = "uuid")
     @ExcelProperty("uuid")
     private String universalId;
 
-    @Schema(description = "申请人id", example = "14065")
+    @Schema(description = "申请人id")
     @ExcelProperty("申请人id")
     private Long employeeId;
 
-    @Schema(description = "申请人uuid", example = "8771")
+    @Schema(description = "申请人uuid")
     @ExcelProperty("申请人uuid")
     private String employeeUuid;
 
-    @Schema(description = "申请员工姓名", example = "芋艿")
+    @Schema(description = "申请员工姓名")
     @ExcelProperty("申请员工姓名")
     private String employeeName;
 
-    @Schema(description = "申请员工手机号", example = "13900000000")
+    @Schema(description = "申请员工手机号")
     private String employeePhone;
 
-    @Schema(description = "申请用户账号id", example = "18283")
+    @Schema(description = "申请用户账号id")
     @ExcelProperty("申请用户账号id")
     private Long userId;
 
-    @Schema(description = "申请用户账号uuid", example = "28769")
+    @Schema(description = "申请用户账号uuid")
     @ExcelProperty("申请用户账号uuid")
     private String userUuid;
 
-    @Schema(description = "申请用户账号手机号", example = "13900000000")
+    @Schema(description = "申请用户账号手机号")
     private String userPhone;
 
-    @Schema(description = "部门id", example = "12910")
+    @Schema(description = "部门id")
     @ExcelProperty("部门id")
     private Long deptId;
 
-    @Schema(description = "部门uuid", example = "18681")
+    @Schema(description = "部门uuid")
     @ExcelProperty("部门uuid")
     private String deptUuid;
 
+    @Schema(description = "部门名称")
+    @ExcelProperty("部门名称")
+    private String deptName;
+
     @Schema(description = "职位")
     @ExcelProperty("职位")
     private String position;
@@ -67,7 +72,7 @@ public class OaUniversalRespVO {
     @ExcelProperty("申请事项标题")
     private String title;
 
-    @Schema(description = "详细描述", example = "随便")
+    @Schema(description = "详细描述")
     @ExcelProperty("详细描述")
     private String description;
 
@@ -75,30 +80,34 @@ public class OaUniversalRespVO {
     @ExcelProperty("备注")
     private String remarks;
 
-    @Schema(description = "流程实例id", example = "18345")
+    @Schema(description = "流程实例id")
     @ExcelProperty("流程实例id")
     private String procInstId;
 
-    @Schema(description = "审核状态(0暂存、1已提交、2审核中、3已审核、4已关闭、5已驳回)", example = "1")
-    @ExcelProperty("审核状态(0暂存、1已提交、2审核中、3已审核、4已关闭、5已驳回)")
+    @Schema(description = "审核状态(0暂存、1已提交、2审核中、3已审核、4已关闭、5已驳回、6已撤回)")
+    @ExcelProperty("审核状态(0暂存、1已提交、2审核中、3已审核、4已关闭、5已驳回、6已撤回)")
     private String auditStatus;
 
-    @Schema(description = "当前审核人用户id", example = "31811")
+    @Schema(description = "当前审核人用户id")
     @ExcelProperty("当前审核人用户id")
     private Long currentAuditUserId;
 
-    @Schema(description = "当前审核人用户uuid", example = "23687")
+    @Schema(description = "当前审核人用户uuid")
     @ExcelProperty("当前审核人用户uuid")
     private String currentAuditUserUuid;
 
-    @Schema(description = "当前审核人员工id", example = "2375")
+    @Schema(description = "当前审核人员工id")
     @ExcelProperty("当前审核人员工id")
     private Long currentAuditEmployeeId;
 
-    @Schema(description = "当前审核人员工uuid", example = "13039")
+    @Schema(description = "当前审核人员工uuid")
     @ExcelProperty("当前审核人员工uuid")
     private String currentAuditEmployeeUuid;
 
+    @Schema(description = "当前审核人员工uuid")
+    @ExcelProperty("当前审核人员工uuid")
+    private String currentAuditEmployeeName;
+
     @Schema(description = "最后审核时间")
     @ExcelProperty("最后审核时间")
     @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@@ -112,7 +121,7 @@ public class OaUniversalRespVO {
     @ExcelProperty("创建者")
     private String creator;
 
-    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Schema(description = "创建时间")
     @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
     @ExcelProperty("创建时间")
     private LocalDateTime createTime;
@@ -123,4 +132,7 @@ public class OaUniversalRespVO {
     @Schema(description = "审批记录列表")
     private List<BpmTaskRespVO> auditRecordList;
 
+    @Schema(description = "审批人员列表")
+    private List<AdminUserRespDTO> auditUserList;
+
 }

+ 0 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/universal/vo/OaUniversalSaveReqVO.java

@@ -32,7 +32,4 @@ public class OaUniversalSaveReqVO {
     @Schema(description = "附件主键id集合", example = "[1, 2]")
     private List<Long> fileIdList;
 
-    @Schema(description = "审批信息")
-    private BpmTaskApproveReqVO bpmTaskApproveReqVO;
-
 }

+ 2 - 2
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java

@@ -13,11 +13,11 @@ public class BpmTaskReturnReqVO {
     @NotEmpty(message = "任务编号不能为空")
     private String id;
 
-    @Schema(description = "回退到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "modifyApply")
+    @Schema(description = "回退到的任务 Key", requiredMode = Schema.RequiredMode.AUTO, example = "modifyApply")
     // @NotEmpty(message = "回退到的任务 Key 不能为空")
     private String targetTaskDefinitionKey;
 
-    @Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回")
+    @Schema(description = "回退意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回,谁同意,谁反对?")
     @NotEmpty(message = "回退意见不能为空")
     private String reason;
 

+ 23 - 26
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/universal/OaUniversalService.java

@@ -6,9 +6,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.oa.universal.vo.OaUniversalR
 import cn.iocoder.yudao.module.bpm.controller.admin.oa.universal.vo.OaUniversalSaveReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskReturnReqVO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.universal.OaUniversalDO;
-
-import javax.validation.Valid;
 
 /**
  * 通用事项审批流程信息 Service 接口
@@ -17,35 +14,34 @@ import javax.validation.Valid;
  */
 public interface OaUniversalService {
 
-    Long commitOaUniversal(OaUniversalSaveReqVO createReqVO);
-
-    Long stagingOaUniversal(OaUniversalSaveReqVO createReqVO);
+    Long commitOaUniversal(OaUniversalSaveReqVO commitReqVO);
 
-    Long agreeOaUniversal(BpmTaskApproveReqVO approveReqVO);
+    Long stagingOaUniversal(OaUniversalSaveReqVO stagingReqVO);
 
-    Long disagreeOaUniversal(BpmTaskReturnReqVO approveReqVO);
+    Long agreeOaUniversal(BpmTaskApproveReqVO agreeReqVO);
 
-    Long revocationOaUniversal(BpmTaskApproveReqVO approveReqVO);
+    Long disagreeOaUniversal(BpmTaskReturnReqVO disagreeReqVO);
 
-    Long completeFalseOaUniversal(Long userId, BpmTaskApproveReqVO reqVO);
+    Long revocationOaUniversal(BpmTaskApproveReqVO revocationReqVO);
 
-    Long updateCompleteOaUniversal(Long userId, @Valid OaUniversalSaveReqVO createReqVO, @Valid BpmTaskApproveReqVO reqVO);
+    Long reCommitOaUniversal(OaUniversalSaveReqVO reCommitReqVO);
 
+    Long closeOaUniversal(OaUniversalSaveReqVO closeReqVO);
 
-    /**
-     * 创建通用事项审批流程信息
-     *
-     * @param createReqVO 创建信息
-     * @return 编号
-     */
-    Long createOaUniversal(@Valid OaUniversalSaveReqVO createReqVO);
-
-    /**
-     * 更新通用事项审批流程信息
-     *
-     * @param updateReqVO 更新信息
-     */
-    void updateOaUniversal(@Valid OaUniversalSaveReqVO updateReqVO);
+    // /**
+    //  * 创建通用事项审批流程信息
+    //  *
+    //  * @param createReqVO 创建信息
+    //  * @return 编号
+    //  */
+    // Long createOaUniversal(@Valid OaUniversalSaveReqVO createReqVO);
+    //
+    // /**
+    //  * 更新通用事项审批流程信息
+    //  *
+    //  * @param updateReqVO 更新信息
+    //  */
+    // void updateOaUniversal(@Valid OaUniversalSaveReqVO updateReqVO);
 
     /**
      * 删除通用事项审批流程信息
@@ -68,6 +64,7 @@ public interface OaUniversalService {
      * @param pageReqVO 分页查询
      * @return 通用事项审批流程信息分页
      */
-    PageResult<OaUniversalDO> getOaUniversalPage(OaUniversalPageReqVO pageReqVO);
+    PageResult<OaUniversalRespVO> getOaUniversalPage(OaUniversalPageReqVO pageReqVO);
 
+    PageResult<OaUniversalRespVO> exportOaUniversalExcel(OaUniversalPageReqVO pageReqVO);
 }

+ 162 - 87
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/universal/OaUniversalServiceImpl.java

@@ -2,6 +2,9 @@ package cn.iocoder.yudao.module.bpm.service.oa.universal;
 
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
@@ -16,11 +19,16 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskReturnRe
 import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.universal.OaUniversalDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.oa.universal.OaUniversalMapper;
 import cn.iocoder.yudao.module.bpm.enums.DictDataConstants;
+import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
 import cn.iocoder.yudao.module.infra.api.file.FileApi;
 import cn.iocoder.yudao.module.infra.api.file.dto.FileDTO;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.commons.lang3.StringUtils;
 import org.flowable.engine.TaskService;
@@ -69,15 +77,18 @@ public class OaUniversalServiceImpl implements OaUniversalService {
     @Resource
     private AdminUserApi adminUserApi;
 
+    @Resource
+    private DeptApi deptApi;
+
     @Override
-    public Long commitOaUniversal(OaUniversalSaveReqVO createReqVO) {
+    public Long commitOaUniversal(OaUniversalSaveReqVO commitReqVO) {
         // 登录人信息
         Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
         AdminUserRespDTO loginUser = adminUserApi.getUser(loginUserId);
         Objects.requireNonNull(loginUser, "登录用户不能为空");
         // TODO DP 根据登录人查询出对应的员工信息
 
-        OaUniversalDO oaUniversal = BeanUtils.toBean(createReqVO, OaUniversalDO.class);
+        OaUniversalDO oaUniversal = BeanUtils.toBean(commitReqVO, OaUniversalDO.class);
         if (StringUtils.isBlank(oaUniversal.getUniversalId())) {
             // 创建单据uuid
             String uuid = IdUtil.fastSimpleUUID();
@@ -99,10 +110,10 @@ public class OaUniversalServiceImpl implements OaUniversalService {
         }
         // 发起流程
         Map<String, Object> processInstanceVariables = new HashMap<>();
-        processInstanceVariables.put("auditPass", true);
+        processInstanceVariables.put("auditPass", "true");
         // 添加审批人信息
         Map<String, List<Long>> startUserSelectAssignees = new HashMap<>();
-        List<Long> selectAssignees = createReqVO.getStartUserSelectAssignees();
+        List<Long> selectAssignees = commitReqVO.getStartUserSelectAssignees();
         startUserSelectAssignees.put("approver", selectAssignees);
         String processInstanceId = processInstanceApi
                 .createProcessInstance(loginUser.getId(),
@@ -118,25 +129,25 @@ public class OaUniversalServiceImpl implements OaUniversalService {
         oaUniversalMapper.updateById(new OaUniversalDO()
                 .setId(oaUniversal.getId())
                 .setProcInstId(processInstanceId)
-                .setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_SUBMITTED)
+                .setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_COMMITTED)
                 .setFinalAuditDate(LocalDateTime.now())
                 .setCurrentAuditEmployeeId(Long.valueOf(task.getAssignee()))
                 .setStartUserSelectAssignees(selectAssignees.stream().map(String::valueOf).collect(Collectors.joining(","))));
         // 保存业务uuid到附件中
-        saveFileList(createReqVO.getFileIdList(), oaUniversal.getUniversalId());
+        saveFileList(commitReqVO.getFileIdList(), oaUniversal.getUniversalId());
         // TODO DP 发送提交成功站内信
         return oaUniversal.getId();
     }
 
     @Override
-    public Long stagingOaUniversal(OaUniversalSaveReqVO createReqVO) {
+    public Long stagingOaUniversal(OaUniversalSaveReqVO stagingReqVO) {
         // 登录人信息
         Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
         AdminUserRespDTO loginUser = adminUserApi.getUser(loginUserId);
         Objects.requireNonNull(loginUser, "登录用户不能为空");
         // TODO DP 根据登录人查询出对应的员工信息
 
-        OaUniversalDO oaUniversal = BeanUtils.toBean(createReqVO, OaUniversalDO.class);
+        OaUniversalDO oaUniversal = BeanUtils.toBean(stagingReqVO, OaUniversalDO.class);
         if (StringUtils.isBlank(oaUniversal.getUniversalId())) {
             // 创建单据uuid
             String uuid = IdUtil.fastSimpleUUID();
@@ -158,21 +169,21 @@ public class OaUniversalServiceImpl implements OaUniversalService {
             oaUniversalMapper.updateById(oaUniversal);
         }
         // 保存业务uuid到附件中
-        saveFileList(createReqVO.getFileIdList(), oaUniversal.getUniversalId());
+        saveFileList(stagingReqVO.getFileIdList(), oaUniversal.getUniversalId());
 
         return oaUniversal.getId();
     }
 
     @Override
-    public Long agreeOaUniversal(BpmTaskApproveReqVO approveReqVO) {
+    public Long agreeOaUniversal(BpmTaskApproveReqVO agreeReqVO) {
         // 登录人信息
         Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
         // TODO DP 根据登录人查询出对应的员工信息
 
-        Task currentTask = bpmTaskService.getTask(approveReqVO.getId());
+        Task currentTask = bpmTaskService.getTask(agreeReqVO.getId());
         // 审批同意
-        approveReqVO.setReason("[同意]" + approveReqVO.getReason());
-        bpmTaskService.approveTask(loginUserId, approveReqVO);
+        agreeReqVO.setReason("[同意]" + agreeReqVO.getReason());
+        bpmTaskService.approveTask(loginUserId, agreeReqVO);
 
         Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
         if (nextTask != null) {
@@ -197,16 +208,16 @@ public class OaUniversalServiceImpl implements OaUniversalService {
     }
 
     @Override
-    public Long disagreeOaUniversal(BpmTaskReturnReqVO returnReqVO) {
+    public Long disagreeOaUniversal(BpmTaskReturnReqVO disagreeReqVO) {
         // 登录人信息
         Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
         // TODO DP 根据登录人查询出对应的员工信息
 
-        Task currentTask = bpmTaskService.getTask(returnReqVO.getId());
+        Task currentTask = bpmTaskService.getTask(disagreeReqVO.getId());
 
-        returnReqVO.setTargetTaskDefinitionKey("modifyApply");
-        returnReqVO.setReason("[驳回]" + returnReqVO.getReason());
-        bpmTaskService.returnTask(loginUserId, returnReqVO);
+        disagreeReqVO.setTargetTaskDefinitionKey("modifyApply");
+        disagreeReqVO.setReason("[驳回]" + disagreeReqVO.getReason());
+        bpmTaskService.returnTask(loginUserId, disagreeReqVO);
 
         Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
 
@@ -214,7 +225,7 @@ public class OaUniversalServiceImpl implements OaUniversalService {
         LambdaUpdateWrapper<OaUniversalDO> updateWrapper = new LambdaUpdateWrapper<>();
         updateWrapper.set(OaUniversalDO::getCurrentAuditEmployeeId, nextTask.getAssignee())
                 .set(OaUniversalDO::getFinalAuditDate, LocalDateTime.now())
-                .set(OaUniversalDO::getAuditStatus, DictDataConstants.OA_AUDIT_STATUS_REJECTED);
+                .set(OaUniversalDO::getAuditStatus, DictDataConstants.OA_AUDIT_STATUS_RETURNED);
         oaUniversalMapper.update(updateWrapper);
 
         // 发送通知
@@ -223,17 +234,27 @@ public class OaUniversalServiceImpl implements OaUniversalService {
     }
 
     @Override
-    public Long revocationOaUniversal(BpmTaskApproveReqVO approveReqVO) {
+    public Long revocationOaUniversal(BpmTaskApproveReqVO revocationReqVO) {
         // 登录人信息
         Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
         // TODO DP 根据登录人查询出对应的员工信息
 
-        Task currentTask = bpmTaskService.getTask(approveReqVO.getId());
+        Task currentTask = bpmTaskService.getTask(revocationReqVO.getId());
+        if (currentTask == null) {
+            throw ServiceExceptionUtil.exception(ErrorCodeConstants.TASK_NOT_EXISTS);
+        }
+        LambdaQueryWrapper<OaUniversalDO> lambdaQueryWrapper = new LambdaQueryWrapper<OaUniversalDO>()
+                .eq(OaUniversalDO::getProcInstId, currentTask.getProcessInstanceId());
+        OaUniversalDO oaUniversalDO = oaUniversalMapper.selectOne(lambdaQueryWrapper);
+        if (!DictDataConstants.OA_AUDIT_STATUS_COMMITTED.equals(oaUniversalDO.getAuditStatus())
+            || !Objects.equals(String.valueOf(loginUserId), oaUniversalDO.getCreator())) {
+            throw ServiceExceptionUtil.exception(ErrorCodeConstants.TASK_REVOCATION_NO_ALLOWED);
+        }
 
-        BpmTaskReturnReqVO returnReqVO = BeanUtils.toBean(approveReqVO, BpmTaskReturnReqVO.class);
+        BpmTaskReturnReqVO returnReqVO = BeanUtils.toBean(revocationReqVO, BpmTaskReturnReqVO.class);
         returnReqVO.setTargetTaskDefinitionKey("modifyApply");
         returnReqVO.setReason("[撤回]" + returnReqVO.getReason());
-        bpmTaskService.returnTask(loginUserId, returnReqVO);
+        bpmTaskService.revocationTask(loginUserId, returnReqVO);
 
         Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
 
@@ -241,7 +262,7 @@ public class OaUniversalServiceImpl implements OaUniversalService {
         LambdaUpdateWrapper<OaUniversalDO> updateWrapper = new LambdaUpdateWrapper<>();
         updateWrapper.set(OaUniversalDO::getCurrentAuditEmployeeId, nextTask.getAssignee())
                 .set(OaUniversalDO::getFinalAuditDate, LocalDateTime.now())
-                .set(OaUniversalDO::getAuditStatus, DictDataConstants.OA_AUDIT_STATUS_REJECTED);
+                .set(OaUniversalDO::getAuditStatus, DictDataConstants.OA_AUDIT_STATUS_RECALLED);
         oaUniversalMapper.update(updateWrapper);
 
         // 发送通知
@@ -250,81 +271,83 @@ public class OaUniversalServiceImpl implements OaUniversalService {
     }
 
     @Override
-    public Long completeFalseOaUniversal(Long userId, BpmTaskApproveReqVO reqVO) {
-
-        Task currentTask = bpmTaskService.getTask(reqVO.getId());
+    public Long reCommitOaUniversal(OaUniversalSaveReqVO reCommitReqVO) {
+        // 登录人信息
+        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+        // TODO DP 根据登录人查询出对应的员工信息
 
-        BpmTaskReturnReqVO reqVO2 = BeanUtils.toBean(reqVO, BpmTaskReturnReqVO.class);
-        reqVO2.setTargetTaskDefinitionKey("modifyApply");
-        bpmTaskService.returnTask(userId, reqVO2);
+        OaUniversalDO oaUniversalNew = BeanUtils.toBean(reCommitReqVO, OaUniversalDO.class);
+        OaUniversalDO oaUniversalOld = oaUniversalMapper.selectById(oaUniversalNew.getId());
+
+        Task currentTask = taskService.createTaskQuery().processInstanceId(oaUniversalOld.getProcInstId()).singleResult();
+        BpmTaskApproveReqVO approveReqVO = new BpmTaskApproveReqVO();
+        approveReqVO.setId(currentTask.getId())
+                .setReason("[再次提交]");
+        if (CollectionUtil.isNotEmpty(reCommitReqVO.getStartUserSelectAssignees())) {
+            // 添加审批人信息
+            Map<String, List<Long>> startUserSelectAssignees = new HashMap<>();
+            List<Long> selectAssignees = reCommitReqVO.getStartUserSelectAssignees();
+            startUserSelectAssignees.put("approver", selectAssignees);
+            taskService.setVariable(currentTask.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
+        }
+        taskService.setVariable(currentTask.getId(), "auditPass", "true");
+        // 再次提交,和审批通过逻辑相同
+        bpmTaskService.approveTask(loginUserId, approveReqVO);
 
         Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
+        oaUniversalNew.setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_COMMITTED)
+                .setCurrentAuditEmployeeId(Long.valueOf(nextTask.getAssignee()))
+                .setFinalAuditDate(LocalDateTime.now());
+        oaUniversalMapper.updateById(oaUniversalNew);
 
-        // 更新单据状态,当前处理人,最后处理时间
-        LambdaUpdateWrapper<OaUniversalDO> updateWrapper = new LambdaUpdateWrapper<>();
-        updateWrapper.set(OaUniversalDO::getCurrentAuditEmployeeId, nextTask.getAssignee())
-                .set(OaUniversalDO::getFinalAuditDate, new Date())
-                .set(OaUniversalDO::getAuditStatus, "0")
-                .eq(OaUniversalDO::getProcInstId, currentTask.getProcessInstanceId());
-        oaUniversalMapper.update(updateWrapper);
-
-        // 发送通知
+        // 保存业务uuid到附件中
+        saveFileList(reCommitReqVO.getFileIdList(), oaUniversalOld.getUniversalId());
 
-        // 返回
         return 1L;
     }
 
     @Override
-    public Long updateCompleteOaUniversal(Long userId, OaUniversalSaveReqVO createReqVO, BpmTaskApproveReqVO reqVO) {
-        // 判断是否已经发起流程
-        Boolean auditPass = true;
-
-        OaUniversalDO oaUniversal = BeanUtils.toBean(createReqVO, OaUniversalDO.class);
-
-        Task currentTask = taskService.createTaskQuery().processInstanceId(oaUniversal.getProcInstId()).singleResult();
-
-        //审批通过
-        bpmTaskService.approveTask(userId, reqVO);
+    public Long closeOaUniversal(OaUniversalSaveReqVO closeReqVO) {
+        // 登录人信息
+        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
+        // TODO DP 根据登录人查询出对应的员工信息
 
-        // 提交
-        if (auditPass) {
-            Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
+        OaUniversalDO oaUniversalNew = BeanUtils.toBean(closeReqVO, OaUniversalDO.class);
+        OaUniversalDO oaUniversalOld = oaUniversalMapper.selectById(oaUniversalNew.getId());
 
-            oaUniversal.setAuditStatus("1");
-            oaUniversal.setCurrentAuditEmployeeId(Long.valueOf(nextTask.getAssignee()));
-            oaUniversal.setFinalAuditDate(LocalDateTime.now());
-            oaUniversalMapper.updateById(oaUniversal);
-
-        } else {
-            OaUniversalDO oaUniversalClose = new OaUniversalDO();
-            oaUniversalClose.setId(oaUniversal.getId());
-            oaUniversalClose.setAuditStatus("4");
-            oaUniversalClose.setCurrentAuditEmployeeId(null);
-            oaUniversalClose.setFinalAuditDate(LocalDateTime.now());
-            oaUniversalMapper.updateById(oaUniversalClose);
-        }
-
-        return oaUniversal.getId();
-    }
+        Task currentTask = taskService.createTaskQuery().processInstanceId(oaUniversalOld.getProcInstId()).singleResult();
+        BpmTaskApproveReqVO approveReqVO = new BpmTaskApproveReqVO();
+        approveReqVO.setId(currentTask.getId())
+                .setReason("[关闭]");
+        taskService.setVariable(currentTask.getId(), "auditPass", "false");
+        // 关闭,使用审批通过的方法实现
+        bpmTaskService.approveTask(loginUserId, approveReqVO);
 
+        oaUniversalOld.setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_CLOSED)
+                .setCurrentAuditEmployeeId(-1L)
+                .setFinalAuditDate(LocalDateTime.now());
+        oaUniversalMapper.updateById(oaUniversalOld);
 
-    @Override
-    public Long createOaUniversal(OaUniversalSaveReqVO createReqVO) {
-        // 插入
-        OaUniversalDO oaUniversal = BeanUtils.toBean(createReqVO, OaUniversalDO.class);
-        oaUniversalMapper.insert(oaUniversal);
-        // 返回
-        return oaUniversal.getId();
+        return 1L;
     }
 
-    @Override
-    public void updateOaUniversal(OaUniversalSaveReqVO updateReqVO) {
-        // 校验存在
-        validateOaUniversalExists(updateReqVO.getId());
-        // 更新
-        OaUniversalDO updateObj = BeanUtils.toBean(updateReqVO, OaUniversalDO.class);
-        oaUniversalMapper.updateById(updateObj);
-    }
+    // @Override
+    // public Long createOaUniversal(OaUniversalSaveReqVO createReqVO) {
+    //     // 插入
+    //     OaUniversalDO oaUniversal = BeanUtils.toBean(createReqVO, OaUniversalDO.class);
+    //     oaUniversalMapper.insert(oaUniversal);
+    //     // 返回
+    //     return oaUniversal.getId();
+    // }
+    //
+    // @Override
+    // public void updateOaUniversal(OaUniversalSaveReqVO updateReqVO) {
+    //     // 校验存在
+    //     validateOaUniversalExists(updateReqVO.getId());
+    //     // 更新
+    //     OaUniversalDO updateObj = BeanUtils.toBean(updateReqVO, OaUniversalDO.class);
+    //     oaUniversalMapper.updateById(updateObj);
+    // }
 
     @Override
     public void deleteOaUniversal(Long id) {
@@ -353,19 +376,71 @@ public class OaUniversalServiceImpl implements OaUniversalService {
         List<BpmTaskRespVO> auditRecordList = bpmTaskService.getAuditRecordListByProcessInstanceId(oaUniversalDO.getProcInstId());
         oaUniversalRespVO.setAuditRecordList(auditRecordList);
 
+        // 审批人
+        String startUserSelectAssignees = oaUniversalDO.getStartUserSelectAssignees();
+        if (StrUtil.isNotEmpty(startUserSelectAssignees)) {
+            List<Long> userIdList = Arrays.stream(startUserSelectAssignees.split(","))
+                    .map(Long::valueOf).collect(Collectors.toList());
+            List<AdminUserRespDTO> userList = adminUserApi.getUserList(userIdList);
+            List<AdminUserRespDTO> auditUserList = new ArrayList<>();
+            for (Long userId : userIdList) {
+                for (AdminUserRespDTO adminUserRespDTO : userList) {
+                    if (Objects.equals(userId, adminUserRespDTO.getId())) {
+                        auditUserList.add(adminUserRespDTO);
+                        break;
+                    }
+                }
+            }
+            oaUniversalRespVO.setAuditUserList(auditUserList);
+        }
+
         return oaUniversalRespVO;
     }
 
     @Override
-    public PageResult<OaUniversalDO> getOaUniversalPage(OaUniversalPageReqVO pageReqVO) {
-        return oaUniversalMapper.selectPage(pageReqVO);
+    public PageResult<OaUniversalRespVO> getOaUniversalPage(OaUniversalPageReqVO pageReqVO) {
+        PageResult<OaUniversalDO> oaUniversalDOPageResult = oaUniversalMapper.selectPage(pageReqVO);
+        PageResult<OaUniversalRespVO> oaUniversalRespVOPageResult = BeanUtils.toBean(oaUniversalDOPageResult, OaUniversalRespVO.class);
+
+        List<OaUniversalRespVO> oaUniversalRespVOList = oaUniversalRespVOPageResult.getList();
+        if (CollectionUtil.isNotEmpty(oaUniversalRespVOList)) {
+            List<Long> employeeIdList = oaUniversalRespVOList.stream().map(OaUniversalRespVO::getCurrentAuditEmployeeId).collect(Collectors.toList());
+            List<AdminUserRespDTO> employeeList = adminUserApi.getUserList(employeeIdList);
+            for (OaUniversalRespVO respVO : oaUniversalRespVOList) {
+                for (AdminUserRespDTO employee : employeeList) {
+                    if (respVO.getCurrentAuditEmployeeId() == employee.getId()) {
+                        respVO.setCurrentAuditEmployeeName(employee.getNickname());
+                        break;
+                    }
+                }
+            }
+
+            List<Long> deptIdList = oaUniversalRespVOList.stream().map(OaUniversalRespVO::getDeptId).collect(Collectors.toList());
+            List<DeptRespDTO> deptList = deptApi.getDeptList(deptIdList);
+            for (OaUniversalRespVO respVO : oaUniversalRespVOList) {
+                for (DeptRespDTO dept : deptList) {
+                    if (respVO.getDeptId() == dept.getId()) {
+                        respVO.setDeptName(dept.getName());
+                        break;
+                    }
+                }
+            }
+        }
+
+        return oaUniversalRespVOPageResult;
+    }
+
+    @Override
+    public PageResult<OaUniversalRespVO> exportOaUniversalExcel(OaUniversalPageReqVO pageReqVO) {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        return getOaUniversalPage(pageReqVO);
     }
 
     /**
      * 保存业务uuid到附件中
      */
     private void saveFileList(List<Long> fileIdList, String serviceId) {
-        if (CollectionUtil.isNotEmpty(fileIdList)) {
+        if (CollectionUtil.isNotEmpty(fileIdList) && StrUtil.isNotEmpty(serviceId)) {
             fileApi.updateFileBiz(fileIdList, serviceId);
         }
     }

+ 8 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java

@@ -143,6 +143,14 @@ public interface BpmTaskService {
      */
     void returnTask(Long userId, BpmTaskReturnReqVO reqVO);
 
+    /**
+     * 将任务撤回到指定的 targetDefinitionKey 位置
+     *
+     * @param userId 用户编号
+     * @param reqVO  回退的任务key和当前所在的任务ID
+     */
+    void revocationTask(Long userId, BpmTaskReturnReqVO reqVO);
+
     /**
      * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批
      *

+ 21 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java

@@ -481,6 +481,27 @@ public class BpmTaskServiceImpl implements BpmTaskService {
         returnTask(task, targetElement, reqVO);
     }
 
+    /**
+     * 回退流程节点时,校验目标任务节点是否可回退
+     *
+     * @return 目标任务节点元素
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void revocationTask(Long userId, BpmTaskReturnReqVO reqVO) {
+        // 1.1 当前任务 task
+        Task task = validateTaskExist(reqVO.getId());
+        if (task.isSuspended()) {
+            throw exception(TASK_IS_PENDING);
+        }
+        // 1.2 校验源头和目标节点的关系,并返回目标元素
+        FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(),
+                reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId());
+
+        // 2. 调用 Flowable 框架的回退逻辑
+        returnTask(task, targetElement, reqVO);
+    }
+
     /**
      * 回退流程节点时,校验目标任务节点是否可回退
      *

+ 171 - 171
yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/oa/universal/OaUniversalServiceImplTest.java

@@ -30,176 +30,176 @@ import static org.junit.jupiter.api.Assertions.*;
  */
 @Import(OaUniversalServiceImpl.class)
 public class OaUniversalServiceImplTest extends BaseDbUnitTest {
-
-    @Resource
-    private OaUniversalServiceImpl oaUniversalService;
-
-    @Resource
-    private OaUniversalMapper oaUniversalMapper;
-
-    @Test
-    public void testCreateOaUniversal_success() {
-        // 准备参数
-        OaUniversalSaveReqVO createReqVO = randomPojo(OaUniversalSaveReqVO.class).setId(null);
-
-        // 调用
-        Long oaUniversalId = oaUniversalService.createOaUniversal(createReqVO);
-        // 断言
-        assertNotNull(oaUniversalId);
-        // 校验记录的属性是否正确
-        OaUniversalDO oaUniversal = oaUniversalMapper.selectById(oaUniversalId);
-        assertPojoEquals(createReqVO, oaUniversal, "id");
-    }
-
-    @Test
-    public void testUpdateOaUniversal_success() {
-        // mock 数据
-        OaUniversalDO dbOaUniversal = randomPojo(OaUniversalDO.class);
-        oaUniversalMapper.insert(dbOaUniversal);// @Sql: 先插入出一条存在的数据
-        // 准备参数
-        OaUniversalSaveReqVO updateReqVO = randomPojo(OaUniversalSaveReqVO.class, o -> {
-            o.setId(dbOaUniversal.getId()); // 设置更新的 ID
-        });
-
-        // 调用
-        oaUniversalService.updateOaUniversal(updateReqVO);
-        // 校验是否更新正确
-        OaUniversalDO oaUniversal = oaUniversalMapper.selectById(updateReqVO.getId()); // 获取最新的
-        assertPojoEquals(updateReqVO, oaUniversal);
-    }
-
-    @Test
-    public void testUpdateOaUniversal_notExists() {
-        // 准备参数
-        OaUniversalSaveReqVO updateReqVO = randomPojo(OaUniversalSaveReqVO.class);
-
-        // 调用, 并断言异常
-        assertServiceException(() -> oaUniversalService.updateOaUniversal(updateReqVO), OA_UNIVERSAL_NOT_EXISTS);
-    }
-
-    @Test
-    public void testDeleteOaUniversal_success() {
-        // mock 数据
-        OaUniversalDO dbOaUniversal = randomPojo(OaUniversalDO.class);
-        oaUniversalMapper.insert(dbOaUniversal);// @Sql: 先插入出一条存在的数据
-        // 准备参数
-        Long id = dbOaUniversal.getId();
-
-        // 调用
-        oaUniversalService.deleteOaUniversal(id);
-       // 校验数据不存在了
-       assertNull(oaUniversalMapper.selectById(id));
-    }
-
-    @Test
-    public void testDeleteOaUniversal_notExists() {
-        // 准备参数
-        Long id = randomLongId();
-
-        // 调用, 并断言异常
-        assertServiceException(() -> oaUniversalService.deleteOaUniversal(id), OA_UNIVERSAL_NOT_EXISTS);
-    }
-
-    @Test
-    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
-    public void testGetOaUniversalPage() {
-       // mock 数据
-       OaUniversalDO dbOaUniversal = randomPojo(OaUniversalDO.class, o -> { // 等会查询到
-           o.setUniversalId(null);
-           o.setEmployeeId(null);
-           o.setEmployeeUuid(null);
-           o.setEmployeeName(null);
-           o.setUserId(null);
-           o.setUserUuid(null);
-           o.setDeptId(null);
-           o.setDeptUuid(null);
-           o.setPosition(null);
-           o.setTitle(null);
-           o.setDescription(null);
-           o.setRemarks(null);
-           o.setProcInstId(null);
-           o.setAuditStatus(null);
-           o.setCurrentAuditUserId(null);
-           o.setCurrentAuditUserUuid(null);
-           o.setCurrentAuditEmployeeId(null);
-           o.setCurrentAuditEmployeeUuid(null);
-           o.setFinalAuditDate(null);
-           o.setInfoSource(null);
-           o.setCreateTime(null);
-       });
-       oaUniversalMapper.insert(dbOaUniversal);
-       // 测试 universalId 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setUniversalId(null)));
-       // 测试 employeeId 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setEmployeeId(null)));
-       // 测试 employeeUuid 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setEmployeeUuid(null)));
-       // 测试 employeeName 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setEmployeeName(null)));
-       // 测试 userId 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setUserId(null)));
-       // 测试 userUuid 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setUserUuid(null)));
-       // 测试 deptId 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setDeptId(null)));
-       // 测试 deptUuid 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setDeptUuid(null)));
-       // 测试 position 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setPosition(null)));
-       // 测试 title 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setTitle(null)));
-       // 测试 description 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setDescription(null)));
-       // 测试 remarks 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setRemarks(null)));
-       // 测试 procInstId 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setProcInstId(null)));
-       // 测试 auditStatus 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setAuditStatus(null)));
-       // 测试 currentAuditUserId 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditUserId(null)));
-       // 测试 currentAuditUserUuid 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditUserUuid(null)));
-       // 测试 currentAuditEmployeeId 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditEmployeeId(null)));
-       // 测试 currentAuditEmployeeUuid 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditEmployeeUuid(null)));
-       // 测试 finalAuditDate 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setFinalAuditDate(null)));
-       // 测试 infoSource 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setInfoSource(null)));
-       // 测试 createTime 不匹配
-       oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCreateTime(null)));
-       // 准备参数
-       OaUniversalPageReqVO reqVO = new OaUniversalPageReqVO();
-       reqVO.setUniversalId(null);
-       reqVO.setEmployeeId(null);
-       reqVO.setEmployeeUuid(null);
-       reqVO.setEmployeeName(null);
-       reqVO.setUserId(null);
-       reqVO.setUserUuid(null);
-       reqVO.setDeptId(null);
-       reqVO.setDeptUuid(null);
-       reqVO.setPosition(null);
-       reqVO.setTitle(null);
-       reqVO.setDescription(null);
-       reqVO.setRemarks(null);
-       reqVO.setProcInstId(null);
-       reqVO.setAuditStatus(null);
-       reqVO.setCurrentAuditUserId(null);
-       reqVO.setCurrentAuditUserUuid(null);
-       reqVO.setCurrentAuditEmployeeId(null);
-       reqVO.setCurrentAuditEmployeeUuid(null);
-       reqVO.setFinalAuditDate(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
-       reqVO.setInfoSource(null);
-       reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
-
-       // 调用
-       PageResult<OaUniversalDO> pageResult = oaUniversalService.getOaUniversalPage(reqVO);
-       // 断言
-       assertEquals(1, pageResult.getTotal());
-       assertEquals(1, pageResult.getList().size());
-       assertPojoEquals(dbOaUniversal, pageResult.getList().get(0));
-    }
+    //
+    // @Resource
+    // private OaUniversalServiceImpl oaUniversalService;
+    //
+    // @Resource
+    // private OaUniversalMapper oaUniversalMapper;
+    //
+    // @Test
+    // public void testCreateOaUniversal_success() {
+    //     // 准备参数
+    //     OaUniversalSaveReqVO createReqVO = randomPojo(OaUniversalSaveReqVO.class).setId(null);
+    //
+    //     // 调用
+    //     Long oaUniversalId = oaUniversalService.createOaUniversal(createReqVO);
+    //     // 断言
+    //     assertNotNull(oaUniversalId);
+    //     // 校验记录的属性是否正确
+    //     OaUniversalDO oaUniversal = oaUniversalMapper.selectById(oaUniversalId);
+    //     assertPojoEquals(createReqVO, oaUniversal, "id");
+    // }
+    //
+    // @Test
+    // public void testUpdateOaUniversal_success() {
+    //     // mock 数据
+    //     OaUniversalDO dbOaUniversal = randomPojo(OaUniversalDO.class);
+    //     oaUniversalMapper.insert(dbOaUniversal);// @Sql: 先插入出一条存在的数据
+    //     // 准备参数
+    //     OaUniversalSaveReqVO updateReqVO = randomPojo(OaUniversalSaveReqVO.class, o -> {
+    //         o.setId(dbOaUniversal.getId()); // 设置更新的 ID
+    //     });
+    //
+    //     // 调用
+    //     oaUniversalService.updateOaUniversal(updateReqVO);
+    //     // 校验是否更新正确
+    //     OaUniversalDO oaUniversal = oaUniversalMapper.selectById(updateReqVO.getId()); // 获取最新的
+    //     assertPojoEquals(updateReqVO, oaUniversal);
+    // }
+    //
+    // @Test
+    // public void testUpdateOaUniversal_notExists() {
+    //     // 准备参数
+    //     OaUniversalSaveReqVO updateReqVO = randomPojo(OaUniversalSaveReqVO.class);
+    //
+    //     // 调用, 并断言异常
+    //     assertServiceException(() -> oaUniversalService.updateOaUniversal(updateReqVO), OA_UNIVERSAL_NOT_EXISTS);
+    // }
+    //
+    // @Test
+    // public void testDeleteOaUniversal_success() {
+    //     // mock 数据
+    //     OaUniversalDO dbOaUniversal = randomPojo(OaUniversalDO.class);
+    //     oaUniversalMapper.insert(dbOaUniversal);// @Sql: 先插入出一条存在的数据
+    //     // 准备参数
+    //     Long id = dbOaUniversal.getId();
+    //
+    //     // 调用
+    //     oaUniversalService.deleteOaUniversal(id);
+    //    // 校验数据不存在了
+    //    assertNull(oaUniversalMapper.selectById(id));
+    // }
+    //
+    // @Test
+    // public void testDeleteOaUniversal_notExists() {
+    //     // 准备参数
+    //     Long id = randomLongId();
+    //
+    //     // 调用, 并断言异常
+    //     assertServiceException(() -> oaUniversalService.deleteOaUniversal(id), OA_UNIVERSAL_NOT_EXISTS);
+    // }
+    //
+    // @Test
+    // @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
+    // public void testGetOaUniversalPage() {
+    //    // mock 数据
+    //    OaUniversalDO dbOaUniversal = randomPojo(OaUniversalDO.class, o -> { // 等会查询到
+    //        o.setUniversalId(null);
+    //        o.setEmployeeId(null);
+    //        o.setEmployeeUuid(null);
+    //        o.setEmployeeName(null);
+    //        o.setUserId(null);
+    //        o.setUserUuid(null);
+    //        o.setDeptId(null);
+    //        o.setDeptUuid(null);
+    //        o.setPosition(null);
+    //        o.setTitle(null);
+    //        o.setDescription(null);
+    //        o.setRemarks(null);
+    //        o.setProcInstId(null);
+    //        o.setAuditStatus(null);
+    //        o.setCurrentAuditUserId(null);
+    //        o.setCurrentAuditUserUuid(null);
+    //        o.setCurrentAuditEmployeeId(null);
+    //        o.setCurrentAuditEmployeeUuid(null);
+    //        o.setFinalAuditDate(null);
+    //        o.setInfoSource(null);
+    //        o.setCreateTime(null);
+    //    });
+    //    oaUniversalMapper.insert(dbOaUniversal);
+    //    // 测试 universalId 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setUniversalId(null)));
+    //    // 测试 employeeId 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setEmployeeId(null)));
+    //    // 测试 employeeUuid 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setEmployeeUuid(null)));
+    //    // 测试 employeeName 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setEmployeeName(null)));
+    //    // 测试 userId 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setUserId(null)));
+    //    // 测试 userUuid 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setUserUuid(null)));
+    //    // 测试 deptId 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setDeptId(null)));
+    //    // 测试 deptUuid 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setDeptUuid(null)));
+    //    // 测试 position 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setPosition(null)));
+    //    // 测试 title 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setTitle(null)));
+    //    // 测试 description 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setDescription(null)));
+    //    // 测试 remarks 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setRemarks(null)));
+    //    // 测试 procInstId 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setProcInstId(null)));
+    //    // 测试 auditStatus 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setAuditStatus(null)));
+    //    // 测试 currentAuditUserId 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditUserId(null)));
+    //    // 测试 currentAuditUserUuid 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditUserUuid(null)));
+    //    // 测试 currentAuditEmployeeId 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditEmployeeId(null)));
+    //    // 测试 currentAuditEmployeeUuid 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCurrentAuditEmployeeUuid(null)));
+    //    // 测试 finalAuditDate 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setFinalAuditDate(null)));
+    //    // 测试 infoSource 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setInfoSource(null)));
+    //    // 测试 createTime 不匹配
+    //    oaUniversalMapper.insert(cloneIgnoreId(dbOaUniversal, o -> o.setCreateTime(null)));
+    //    // 准备参数
+    //    OaUniversalPageReqVO reqVO = new OaUniversalPageReqVO();
+    //    reqVO.setUniversalId(null);
+    //    reqVO.setEmployeeId(null);
+    //    reqVO.setEmployeeUuid(null);
+    //    reqVO.setEmployeeName(null);
+    //    reqVO.setUserId(null);
+    //    reqVO.setUserUuid(null);
+    //    reqVO.setDeptId(null);
+    //    reqVO.setDeptUuid(null);
+    //    reqVO.setPosition(null);
+    //    reqVO.setTitle(null);
+    //    reqVO.setDescription(null);
+    //    reqVO.setRemarks(null);
+    //    reqVO.setProcInstId(null);
+    //    reqVO.setAuditStatus(null);
+    //    reqVO.setCurrentAuditUserId(null);
+    //    reqVO.setCurrentAuditUserUuid(null);
+    //    reqVO.setCurrentAuditEmployeeId(null);
+    //    reqVO.setCurrentAuditEmployeeUuid(null);
+    //    reqVO.setFinalAuditDate(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+    //    reqVO.setInfoSource(null);
+    //    reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
+    //
+    //    // 调用
+    //    PageResult<OaUniversalDO> pageResult = oaUniversalService.getOaUniversalPage(reqVO);
+    //    // 断言
+    //    assertEquals(1, pageResult.getTotal());
+    //    assertEquals(1, pageResult.getList().size());
+    //    assertPojoEquals(dbOaUniversal, pageResult.getList().get(0));
+    // }
 
 }

+ 4 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java

@@ -40,5 +40,9 @@ public class AdminUserRespDTO {
      * 手机号码
      */
     private String mobile;
+    /**
+     * 用户头像
+     */
+    private String avatar;
 
 }