Browse Source

BPM 模型重构 7:任务分配规则的前端,增加指定用户、自定义脚本等

YunaiV 3 years ago
parent
commit
f46090243f
23 changed files with 391 additions and 12 deletions
  1. 15 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmUserGroupController.java
  2. 21 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmUserGroupSimpleRespVO.java
  3. 3 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/definition/BpmUserGroupConvert.java
  4. 6 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/definition/BpmUserGroupMapper.java
  5. 2 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java
  6. 7 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/package-info.java
  7. 26 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java
  8. 7 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/listener/package-info.java
  9. 17 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmUserGroupService.java
  10. 26 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmTaskAssignRuleServiceImpl.java
  11. 34 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmUserGroupServiceImpl.java
  12. 6 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/dict/SysDictDataMapper.java
  13. 3 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysDictTypeConstants.java
  14. 2 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java
  15. 9 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/SysPostService.java
  16. 26 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysPostServiceImpl.java
  17. 11 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dict/SysDictDataService.java
  18. 24 3
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dict/impl/SysDictDataServiceImpl.java
  19. 10 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java
  20. 25 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java
  21. 8 0
      yudao-admin-ui/src/api/bpm/userGroup.js
  22. 5 0
      yudao-admin-ui/src/utils/dict.js
  23. 98 3
      yudao-admin-ui/src/views/bpm/taskAssignRule/taskAssignRuleDialog.vue

+ 15 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/BpmUserGroupController.java

@@ -7,6 +7,10 @@ import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.group.B
 import cn.iocoder.yudao.adminserver.modules.bpm.convert.definition.BpmUserGroupConvert;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmUserGroupDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmUserGroupService;
+import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.SysUserSimpleRespVO;
+import cn.iocoder.yudao.adminserver.modules.system.convert.user.SysUserConvert;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import io.swagger.annotations.Api;
@@ -19,6 +23,8 @@ import org.springframework.web.bind.annotation.*;
 import javax.annotation.Resource;
 import javax.validation.Valid;
 
+import java.util.List;
+
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
 @Api(tags = "用户组")
@@ -71,4 +77,13 @@ public class BpmUserGroupController {
         return success(BpmUserGroupConvert.INSTANCE.convertPage(pageResult));
     }
 
+    @GetMapping("/list-all-simple")
+    @ApiOperation(value = "获取用户组精简信息列表", notes = "只包含被开启的用户组,主要用于前端的下拉选项")
+    public CommonResult<List<BpmUserGroupRespVO>> getSimpleUserGroups() {
+        // 获用户门列表,只要开启状态的
+        List<BpmUserGroupDO> list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus());
+        // 排序后,返回给前端
+        return success(BpmUserGroupConvert.INSTANCE.convertList2(list));
+    }
+
 }

+ 21 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/controller/definition/vo/rule/BpmUserGroupSimpleRespVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.rule;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@ApiModel("用户组精简信息 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class BpmUserGroupSimpleRespVO {
+
+    @ApiModelProperty(value = "用户组编号", required = true, example = "1024")
+    private Long id;
+
+    @ApiModelProperty(value = "用户组名字", required = true, example = "芋道")
+    private String name;
+
+}

+ 3 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/convert/definition/BpmUserGroupConvert.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmUse
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 
 import org.mapstruct.Mapper;
+import org.mapstruct.Named;
 import org.mapstruct.factory.Mappers;
 
 /**
@@ -31,4 +32,6 @@ public interface BpmUserGroupConvert {
 
     PageResult<BpmUserGroupRespVO> convertPage(PageResult<BpmUserGroupDO> page);
 
+    @Named("convertList2")
+    List<BpmUserGroupRespVO> convertList2(List<BpmUserGroupDO> list);
 }

+ 6 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/dal/mysql/definition/BpmUserGroupMapper.java

@@ -7,6 +7,8 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * 用户组 Mapper
  *
@@ -23,4 +25,8 @@ public interface BpmUserGroupMapper extends BaseMapperX<BpmUserGroupDO> {
                 .orderByDesc("id"));
     }
 
+    default List<BpmUserGroupDO> selectListByStatus(Integer status) {
+        return selectList("status", status);
+    }
+
 }

+ 2 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/enums/BpmErrorCodeConstants.java

@@ -54,4 +54,6 @@ public interface BpmErrorCodeConstants {
 
     // ========== 用户组模块 1-009-011-000 ==========
     ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1009011000, "用户组不存在");
+    ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1009011001, "名字为【{}】的用户组已被禁用");
+
 }

+ 7 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/package-info.java

@@ -0,0 +1,7 @@
+/**
+ * 拓展 {@link org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior} 实现,基于 {@link cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmTaskAssignRuleDO} 实现自定义的任务分配规则。
+ * 原因:BPMN 默认的 assign、candidateUsers、candidateGroups 拓展起来有一定的难度,所以选择放弃它们,使用自己定义的规则。
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior;

+ 26 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/behavior/script/BpmTaskAssignScript.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.behavior.script;
+
+import org.activiti.engine.impl.persistence.entity.TaskEntity;
+
+import java.util.List;
+
+/**
+ * Bpm 任务分配的自定义 Script 脚本
+ * 使用场景:
+ * 1. 设置审批人为发起人
+ * 2. 设置审批人为发起人的 Leader
+ * 3. 甚至审批人为发起人的 Leader 的 Leader
+ *
+ * @author 芋道源码
+ */
+public interface BpmTaskAssignScript {
+
+    /**
+     * 基于流程任务,获得任务的候选用户们
+     *
+     * @param task 任务
+     * @return 候选人用户的编号数组
+     */
+    List<Long> calculateTaskCandidateUsers(TaskEntity task);
+
+}

+ 7 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/framework/activiti/core/listener/package-info.java

@@ -0,0 +1,7 @@
+/**
+ * 自定义各种 Activiti 的监听器,实现流程示例、流程任务的拓展表信息的同步
+ * 例如说,{@link org.activiti.api.task.model.Task} 新建时,我们也要新建对应的 {@link cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.task.BpmTaskExtDO} 记录
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.adminserver.modules.bpm.framework.activiti.core.listener;

+ 17 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/BpmUserGroupService.java

@@ -54,6 +54,14 @@ public interface BpmUserGroupService {
      */
     List<BpmUserGroupDO> getUserGroupList(Collection<Long> ids);
 
+    /**
+     * 获得指定状态的用户组列表
+     *
+     * @param status 状态
+     * @return 用户组列表
+     */
+    List<BpmUserGroupDO> getUserGroupListByStatus(Integer status);
+
     /**
      * 获得用户组分页
      *
@@ -62,4 +70,13 @@ public interface BpmUserGroupService {
      */
     PageResult<BpmUserGroupDO> getUserGroupPage(BpmUserGroupPageReqVO pageReqVO);
 
+    /**
+     * 校验用户组们是否有效。如下情况,视为无效:
+     * 1. 用户组编号不存在
+     * 2. 用户组被禁用
+     *
+     * @param ids 用户组编号数组
+     */
+    void validUserGroups(Set<Long> ids);
+
 }

+ 26 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmTaskAssignRuleServiceImpl.java

@@ -12,9 +12,15 @@ import cn.iocoder.yudao.adminserver.modules.bpm.enums.definition.BpmTaskAssignRu
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmModelService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmTaskAssignRuleService;
+import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmUserGroupService;
+import cn.iocoder.yudao.adminserver.modules.system.enums.SysDictTypeConstants;
 import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
+import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysPostService;
+import cn.iocoder.yudao.adminserver.modules.system.service.dict.SysDictDataService;
 import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysRoleService;
+import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
 import cn.iocoder.yudao.framework.activiti.core.util.ActivitiUtils;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.activiti.bpmn.model.BpmnModel;
@@ -30,6 +36,7 @@ import java.util.Objects;
 import java.util.Set;
 
 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.*;
+import static cn.iocoder.yudao.adminserver.modules.system.enums.SysDictTypeConstants.BPM_TASK_ASSIGN_RULE_TYPE;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**
@@ -53,6 +60,14 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
     private SysRoleService roleService;
     @Resource
     private SysDeptService deptService;
+    @Resource
+    private SysPostService postService;
+    @Resource
+    private SysUserService userService;
+    @Resource
+    private BpmUserGroupService userGroupService;
+    @Resource
+    private SysDictDataService dictDataService;
 
     @Override
     public List<BpmTaskAssignRuleDO> getTaskAssignRuleListByProcessDefinitionId(String processDefinitionId,
@@ -146,8 +161,18 @@ public class BpmTaskAssignRuleServiceImpl implements BpmTaskAssignRuleService {
         } else if (ObjectUtils.equalsAny(BpmTaskAssignRuleTypeEnum.DEPT_MEMBER.getType(),
                 BpmTaskAssignRuleTypeEnum.DEPT_LEADER.getType())) {
             deptService.validDepts(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.POST.getType())) {
+            postService.validPosts(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER.getType())) {
+            userService.validUsers(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.USER_GROUP.getType())) {
+            userGroupService.validUserGroups(options);
+        } else if (Objects.equals(type, BpmTaskAssignRuleTypeEnum.SCRIPT.getType())) {
+            dictDataService.validDictDatas(SysDictTypeConstants.BPM_TASK_ASSIGN_SCRIPT,
+                    CollectionUtils.convertSet(options, String::valueOf));
+        } else {
+            throw new IllegalArgumentException(StrUtil.format("未知的规则类型({})", type));
         }
-        // TODO 其它的
     }
 
 }

+ 34 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/bpm/service/definition/impl/BpmUserGroupServiceImpl.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.adminserver.modules.bpm.service.definition.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.group.BpmUserGroupCreateReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.group.BpmUserGroupPageReqVO;
 import cn.iocoder.yudao.adminserver.modules.bpm.controller.definition.vo.group.BpmUserGroupUpdateReqVO;
@@ -7,15 +8,23 @@ import cn.iocoder.yudao.adminserver.modules.bpm.convert.definition.BpmUserGroupC
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.dataobject.definition.BpmUserGroupDO;
 import cn.iocoder.yudao.adminserver.modules.bpm.dal.mysql.definition.BpmUserGroupMapper;
 import cn.iocoder.yudao.adminserver.modules.bpm.service.definition.BpmUserGroupService;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
+import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.USER_GROUP_IS_DISABLE;
 import static cn.iocoder.yudao.adminserver.modules.bpm.enums.BpmErrorCodeConstants.USER_GROUP_NOT_EXISTS;
+import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.USER_IS_DISABLE;
+import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.USER_NOT_EXISTS;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**
@@ -72,9 +81,34 @@ public class BpmUserGroupServiceImpl implements BpmUserGroupService {
         return userGroupMapper.selectBatchIds(ids);
     }
 
+    @Override
+    public List<BpmUserGroupDO> getUserGroupListByStatus(Integer status) {
+        return userGroupMapper.selectListByStatus(status);
+    }
+
     @Override
     public PageResult<BpmUserGroupDO> getUserGroupPage(BpmUserGroupPageReqVO pageReqVO) {
         return userGroupMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public void validUserGroups(Set<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return;
+        }
+        // 获得用户组信息
+        List<BpmUserGroupDO> userGroups = userGroupMapper.selectBatchIds(ids);
+        Map<Long, BpmUserGroupDO> userGroupMap = CollectionUtils.convertMap(userGroups, BpmUserGroupDO::getId);
+        // 校验
+        ids.forEach(id -> {
+            BpmUserGroupDO userGroup = userGroupMap.get(id);
+            if (userGroup == null) {
+                throw exception(USER_GROUP_NOT_EXISTS);
+            }
+            if (!CommonStatusEnum.ENABLE.getStatus().equals(userGroup.getStatus())) {
+                throw exception(USER_GROUP_IS_DISABLE, userGroup.getName());
+            }
+        });
+    }
+
 }

+ 6 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/dict/SysDictDataMapper.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 
@@ -20,6 +21,11 @@ public interface SysDictDataMapper extends BaseMapperX<SysDictDataDO> {
                 .eq("value", value));
     }
 
+    default List<SysDictDataDO> selectByDictTypeAndValues(String dictType, Collection<String> values) {
+        return selectList(new QueryWrapper<SysDictDataDO>().eq("dict_type", dictType)
+                .in("value", values));
+    }
+
     default int selectCountByDictType(String dictType) {
         return selectCount("dict_type", dictType);
     }

+ 3 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysDictTypeConstants.java

@@ -22,4 +22,7 @@ public interface SysDictTypeConstants {
     String SMS_SEND_STATUS = "sys_sms_send_status"; // 短信发送状态
     String SMS_RECEIVE_STATUS = "sys_sms_receive_status"; // 短信接收状态
 
+    String BPM_TASK_ASSIGN_RULE_TYPE = "bpm_task_assign_rule_type"; // 任务分配规则类型
+    String BPM_TASK_ASSIGN_SCRIPT = "bpm_task_assign_script"; // 任务分配自定义脚本
+
 }

+ 2 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java

@@ -40,6 +40,7 @@ public interface SysErrorCodeConstants {
     ErrorCode USER_NOT_EXISTS = new ErrorCode(1002004003, "用户不存在");
     ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002004004, "导入用户数据不能为空!");
     ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1002004005, "用户密码校验失败");
+    ErrorCode USER_IS_DISABLE = new ErrorCode(1002003004, "名字为【{}】的用户已被禁用");
 
     // ========== 部门模块 1002005000 ==========
     ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1002004001, "已经存在该名字的部门");
@@ -66,7 +67,7 @@ public interface SysErrorCodeConstants {
 
     // ========== 字典数据 1002007000 ==========
     ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1002007001, "当前字典数据不存在");
-    ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1002007002, "字典数据不处于开启状态,不允许选择");
+    ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1002007002, "字典数据({})不处于开启状态,不允许选择");
     ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1002007003, "已经存在该值的字典数据");
 
     // ========== 通知公告 1002008000 ==========

+ 9 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/SysPostService.java

@@ -86,4 +86,13 @@ public interface SysPostService {
      */
     SysPostDO getPost(Long id);
 
+    /**
+     * 校验岗位们是否有效。如下情况,视为无效:
+     * 1. 岗位编号不存在
+     * 2. 岗位被禁用
+     *
+     * @param ids 岗位编号数组
+     */
+    void validPosts(Collection<Long> ids);
+
 }

+ 26 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dept/impl/SysPostServiceImpl.java

@@ -1,5 +1,8 @@
 package cn.iocoder.yudao.adminserver.modules.system.service.dept.impl;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysDeptDO;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.adminserver.modules.system.controller.dept.vo.post.SysPostCreateReqVO;
@@ -10,14 +13,17 @@ import cn.iocoder.yudao.adminserver.modules.system.convert.dept.SysPostConvert;
 import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.dept.SysPostMapper;
 import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.dept.SysPostDO;
 import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysPostService;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.*;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**
  * 岗位 Service 实现类
@@ -78,6 +84,26 @@ public class SysPostServiceImpl implements SysPostService {
         return postMapper.selectById(id);
     }
 
+    @Override
+    public void validPosts(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return;
+        }
+        // 获得岗位信息
+        List<SysPostDO> posts = postMapper.selectBatchIds(ids);
+        Map<Long, SysPostDO> postMap = CollectionUtils.convertMap(posts, SysPostDO::getId);
+        // 校验
+        ids.forEach(id -> {
+            SysPostDO post = postMap.get(id);
+            if (post == null) {
+                throw exception(POST_NOT_FOUND);
+            }
+            if (!CommonStatusEnum.ENABLE.getStatus().equals(post.getStatus())) {
+                throw exception(POST_NOT_ENABLE, post.getName());
+            }
+        });
+    }
+
     private void checkCreateOrUpdate(Long id, String name, String code) {
         // 校验自己存在
         checkPostExists(id);

+ 11 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dict/SysDictDataService.java

@@ -8,6 +8,7 @@ import cn.iocoder.yudao.adminserver.modules.system.controller.dict.vo.data.SysDi
 import cn.iocoder.yudao.adminserver.modules.system.controller.dict.vo.data.SysDictDataPageReqVO;
 import cn.iocoder.yudao.adminserver.modules.system.controller.dict.vo.data.SysDictDataUpdateReqVO;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -78,4 +79,14 @@ public interface SysDictDataService {
      */
     int countByDictType(String dictType);
 
+    /**
+     * 校验字典数据们是否有效。如下情况,视为无效:
+     * 1. 字典数据不存在
+     * 2. 字典数据被禁用
+     *
+     * @param dictType 字典类型
+     * @param values 字典数据值的数组
+     */
+    void validDictDatas(String dictType, Collection<String> values);
+
 }

+ 24 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/dict/impl/SysDictDataServiceImpl.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.adminserver.modules.system.service.dict.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.adminserver.modules.system.controller.dict.vo.data.SysDictDataCreateReqVO;
 import cn.iocoder.yudao.adminserver.modules.system.controller.dict.vo.data.SysDictDataExportReqVO;
 import cn.iocoder.yudao.adminserver.modules.system.controller.dict.vo.data.SysDictDataPageReqVO;
@@ -11,16 +12,16 @@ import cn.iocoder.yudao.adminserver.modules.system.mq.producer.dict.SysDictDataP
 import cn.iocoder.yudao.adminserver.modules.system.service.dict.SysDictDataService;
 import cn.iocoder.yudao.adminserver.modules.system.service.dict.SysDictTypeService;
 import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.dict.SysDictDataDO;
+import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -133,6 +134,26 @@ public class SysDictDataServiceImpl implements SysDictDataService {
         return dictDataMapper.selectCountByDictType(dictType);
     }
 
+    @Override
+    public void validDictDatas(String dictType, Collection<String> values) {
+        if (CollUtil.isEmpty(values)) {
+            return;
+        }
+        // 获得字典数据信息
+        List<SysDictDataDO> dictDatas = dictDataMapper.selectByDictTypeAndValues(dictType, values);
+        Map<String, SysDictDataDO> dictDataMap = CollectionUtils.convertMap(dictDatas, SysDictDataDO::getValue);
+        // 校验
+        values.forEach(value -> {
+            SysDictDataDO dictData = dictDataMap.get(value);
+            if (dictData == null) {
+                throw exception(DICT_DATA_NOT_EXISTS);
+            }
+            if (!CommonStatusEnum.ENABLE.getStatus().equals(dictData.getStatus())) {
+                throw exception(DICT_DATA_NOT_ENABLE, dictData.getLabel());
+            }
+        });
+    }
+
     private void checkCreateOrUpdate(Long id, String value, String dictType) {
         // 校验自己存在
         checkDictDataExists(id);

+ 10 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserService.java

@@ -9,10 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 
 import java.io.InputStream;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * 后台用户 Service 接口
@@ -177,4 +174,13 @@ public interface SysUserService {
      */
     List<SysUserDO> getUsersByStatus(Integer status);
 
+    /**
+     * 校验用户们是否有效。如下情况,视为无效:
+     * 1. 用户编号不存在
+     * 2. 用户被禁用
+     *
+     * @param ids 用户编号数组
+     */
+    void validUsers(Set<Long> ids);
+
 }

+ 25 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/impl/SysUserServiceImpl.java

@@ -182,6 +182,11 @@ public class SysUserServiceImpl implements SysUserService {
         return userMapper.selectBatchIds(ids);
     }
 
+    @Override
+    public Map<Long, SysUserDO> getUserMap(Collection<Long> ids) {
+        return SysUserService.super.getUserMap(ids);
+    }
+
     @Override
     public List<SysUserDO> getUsersByNickname(String nickname) {
         return userMapper.selectListByNickname(nickname);
@@ -384,4 +389,24 @@ public class SysUserServiceImpl implements SysUserService {
         return userMapper.selectListByStatus(status);
     }
 
+    @Override
+    public void validUsers(Set<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return;
+        }
+        // 获得岗位信息
+        List<SysUserDO> users = userMapper.selectBatchIds(ids);
+        Map<Long, SysUserDO> userMap = CollectionUtils.convertMap(users, SysUserDO::getId);
+        // 校验
+        ids.forEach(id -> {
+            SysUserDO user = userMap.get(id);
+            if (user == null) {
+                throw exception(USER_NOT_EXISTS);
+            }
+            if (!CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) {
+                throw exception(USER_IS_DISABLE, user.getNickname());
+            }
+        });
+    }
+
 }

+ 8 - 0
yudao-admin-ui/src/api/bpm/userGroup.js

@@ -42,3 +42,11 @@ export function getUserGroupPage(query) {
     params: query
   })
 }
+
+// 获取用户组精简信息列表
+export function listSimpleUserGroups() {
+  return request({
+    url: '/bpm/user-group/list-all-simple',
+    method: 'get'
+  })
+}

+ 5 - 0
yudao-admin-ui/src/utils/dict.js

@@ -40,6 +40,7 @@ export const DICT_TYPE = {
   BPM_TASK_ASSIGN_RULE_TYPE: 'bpm_task_assign_rule_type',
   BPM_PROCESS_INSTANCE_STATUS: 'bpm_process_instance_status',
   BPM_PROCESS_INSTANCE_RESULT: 'bpm_process_instance_result',
+  BPM_TASK_ASSIGN_SCRIPT: 'bpm_task_assign_script',
   OA_LEAVE_STATUS: 'flow_status', // todo 芋艿:可以删除
   OA_LEAVE_TYPE: 'oa_leave_type'
 }
@@ -51,6 +52,10 @@ export const DICT_TYPE = {
  * @returns {*|Array} 数据字典数组
  */
 export function getDictDatas(dictType) {
+  // if (dictType === 'bpm_task_assign_script') {
+  //   console.log(store.getters.dict_datas[dictType]);
+  //   debugger
+  // }
   return store.getters.dict_datas[dictType] || []
 }
 

+ 98 - 3
yudao-admin-ui/src/views/bpm/taskAssignRule/taskAssignRuleDialog.vue

@@ -37,7 +37,7 @@
         </el-form-item>
         <el-form-item label="规则类型" prop="type">
           <el-select v-model="form.type" clearable style="width: 100%">
-            <el-option v-for="dict in taskAssignRuleDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
+            <el-option v-for="dict in taskAssignRuleTypeDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
           </el-select>
         </el-form-item>
         <el-form-item v-if="form.type === 10" label="指定角色" prop="roleIds">
@@ -49,6 +49,27 @@
           <treeselect v-model="form.deptIds" :options="deptTreeOptions" multiple flat :defaultExpandLevel="3"
                       placeholder="请选择指定部门" :normalizer="normalizer"/>
         </el-form-item>
+        <el-form-item v-if="form.type === 22" label="指定岗位" prop="postIds">
+          <el-select v-model="form.postIds" multiple clearable style="width: 100%">
+            <el-option v-for="item in postOptions" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
+          </el-select>
+        </el-form-item>
+        <el-form-item v-if="form.type === 30" label="指定用户" prop="userIds">
+          <el-select v-model="form.userIds" multiple clearable style="width: 100%">
+            <el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)" />
+          </el-select>
+        </el-form-item>
+        <el-form-item v-if="form.type === 40" label="指定用户组" prop="userGroupIds">
+          <el-select v-model="form.userGroupIds" multiple clearable style="width: 100%">
+            <el-option v-for="item in userGroupOptions" :key="parseInt(item.id)" :label="item.name" :value="parseInt(item.id)" />
+          </el-select>
+        </el-form-item>
+        <el-form-item v-if="form.type === 50" label="指定脚本" prop="scripts">
+          <el-select v-model="form.scripts" multiple clearable style="width: 100%">
+            <el-option v-for="dict in taskAssignScriptDictDatas" :key="parseInt(dict.value)"
+                       :label="dict.label" :value="parseInt(dict.value)"/>
+          </el-select>
+        </el-form-item>
       </el-form>
       <div slot="footer" class="dialog-footer">
         <el-button type="primary" @click="submitAssignRuleForm">确 定</el-button>
@@ -66,6 +87,9 @@ import {listSimpleDepts} from "@/api/system/dept";
 
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import {listSimplePosts} from "@/api/system/post";
+import {listSimpleUsers} from "@/api/system/user";
+import {listSimpleUserGroups} from "@/api/bpm/userGroup";
 
 export default {
   name: "taskAssignRuleDialog",
@@ -89,16 +113,24 @@ export default {
         type: [{ required: true, message: "规则类型不能为空", trigger: "change" }],
         roleIds: [{required: true, message: "指定角色不能为空", trigger: "change" }],
         deptIds: [{required: true, message: "指定部门不能为空", trigger: "change" }],
+        postIds: [{required: true, message: "指定岗位不能为空", trigger: "change"}],
+        userIds: [{required: true, message: "指定用户不能为空", trigger: "change"}],
+        userGroupIds: [{required: true, message: "指定用户组不能为空", trigger: "change"}],
+        scripts: [{required: true, message: "指定脚本不能为空", trigger: "change"}],
       },
 
       // 各种下拉框
       roleOptions: [],
       deptOptions: [],
       deptTreeOptions: [],
+      postOptions: [],
+      userOptions: [],
+      userGroupOptions: [],
 
       // 数据字典
       modelFormTypeDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_FORM_TYPE),
-      taskAssignRuleDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE),
+      taskAssignRuleTypeDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE),
+      taskAssignScriptDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_SCRIPT),
     };
   },
   methods: {
@@ -132,10 +164,24 @@ export default {
       this.deptOptions = [];
       this.deptTreeOptions = [];
       listSimpleDepts().then(response => {
-        // 处理 roleOptions 参数
         this.deptOptions.push(...response.data);
         this.deptTreeOptions.push(...this.handleTree(response.data, "id"));
       });
+      // 获得岗位列表
+      this.postOptions = [];
+      listSimplePosts().then(response => {
+        this.postOptions.push(...response.data);
+      });
+      // 获得用户列表
+      this.userOptions = [];
+      listSimpleUsers().then(response => {
+        this.userOptions.push(...response.data);
+      });
+      // 获得用户组列表
+      this.userGroupOptions = [];
+      listSimpleUserGroups().then(response => {
+        this.userGroupOptions.push(...response.data);
+      });
     },
     /** 获得任务分配规则列表 */
     getList() {
@@ -157,12 +203,24 @@ export default {
         options: [],
         roleIds: [],
         deptIds: [],
+        postIds: [],
+        userIds: [],
+        userGroupIds: [],
+        scripts: [],
       };
       // 将 options 赋值到对应的 roleIds 等选项
       if (row.type === 10) {
         this.form.roleIds.push(...row.options);
       } else if (row.type === 20 || row.type === 21) {
         this.form.deptIds.push(...row.options);
+      } else if (row.type === 22) {
+        this.form.postIds.push(...row.options);
+      } else if (row.type === 30) {
+        this.form.userIds.push(...row.options);
+      } else if (row.type === 40) {
+        this.form.userGroupIds.push(...row.options);
+      } else if (row.type === 50) {
+        this.form.scripts.push(...row.options);
       }
       this.open = true;
     },
@@ -180,9 +238,21 @@ export default {
             form.options = form.roleIds;
           } else if (form.type === 20 || form.type === 21) {
             form.options = form.deptIds;
+          } else if (form.type === 22) {
+            form.options = form.postIds;
+          } else if (form.type === 30) {
+            form.options = form.userIds;
+          } else if (form.type === 40) {
+            form.options = form.userGroupIds;
+          } else if (form.type === 50) {
+            form.options = form.scripts;
           }
           form.roleIds = undefined;
           form.deptIds = undefined;
+          form.postIds = undefined;
+          form.userIds = undefined;
+          form.userGroupIds = undefined;
+          form.scripts = undefined;
           // 新增
           if (!form.id) {
             form.modelId = this.modelId; // 模型编号
@@ -226,6 +296,31 @@ export default {
             return deptOption.name;
           }
         }
+      } else if (type === 22) {
+        for (const postOption of this.postOptions) {
+          if (postOption.id === option) {
+            return postOption.name;
+          }
+        }
+      } else if (type === 30) {
+        for (const userOption of this.userOptions) {
+          if (userOption.id === option) {
+            return userOption.nickname;
+          }
+        }
+      } else if (type === 40) {
+        for (const userGroupOption of this.userGroupOptions) {
+          if (userGroupOption.id === option) {
+            return userGroupOption.name;
+          }
+        }
+      } else if (type === 50) {
+        option = option + ''; // 转换成 string
+        for (const dictData of this.taskAssignScriptDictDatas) {
+          if (dictData.value === option) {
+            return dictData.label;
+          }
+        }
       }
       return '未知(' + option + ')';
     },