浏览代码

仿钉钉流程设计- 流程表单字段权限测试

jason 1 年之前
父节点
当前提交
1e30e4851a

+ 26 - 0
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java

@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.bpm.enums.definition;
+
+import cn.hutool.core.util.ArrayUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * BPM 表单权限的枚举
+ *
+ * @author jason
+ */
+@Getter
+@AllArgsConstructor
+public enum BpmFieldPermissionEnum {
+
+    EDITABLE(1, "可编辑"),
+    READONLY(2, "只读"),
+    HIDE(3, "隐藏");
+
+    private final Integer permission;
+    private final String name;
+
+    public static BpmFieldPermissionEnum valueOf(Integer permission) {
+        return ArrayUtil.firstMatch(item -> item.getPermission().equals(permission), values());
+    }
+}

+ 7 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java

@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
 import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
@@ -19,6 +20,7 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
+import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.bpmn.model.UserTask;
 import org.flowable.engine.history.HistoricProcessInstance;
 import org.flowable.engine.runtime.ProcessInstance;
@@ -50,6 +52,8 @@ public class BpmTaskController {
     private BpmProcessInstanceService processInstanceService;
     @Resource
     private BpmFormService formService;
+    @Resource
+    private BpmProcessDefinitionService bpmProcessDefinitionService;
 
     @Resource
     private AdminUserApi adminUserApi;
@@ -134,8 +138,10 @@ public class BpmTaskController {
         // 获得 Form Map
         Map<Long, BpmFormDO> formMap = formService.getFormMap(
                 convertSet(taskList, task -> NumberUtils.parseLong(task.getFormKey())));
+        // 获得 BpmnModel
+        BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId());
         return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance,
-                formMap, userMap, deptMap));
+                formMap, userMap, deptMap,bpmnModel));
     }
 
     @PutMapping("/approve")

+ 7 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java

@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import org.flowable.bpmn.model.BpmnModel;
 import org.flowable.engine.history.HistoricProcessInstance;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.task.api.Task;
@@ -81,7 +82,8 @@ public interface BpmTaskConvert {
                                                                  HistoricProcessInstance processInstance,
                                                                  Map<Long, BpmFormDO> formMap,
                                                                  Map<Long, AdminUserRespDTO> userMap,
-                                                                 Map<Long, DeptRespDTO> deptMap) {
+                                                                 Map<Long, DeptRespDTO> deptMap,
+                                                                 BpmnModel bpmnModel) {
         List<BpmTaskRespVO> taskVOList = CollectionUtils.convertList(taskList, task -> {
             BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class);
             taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task));
@@ -92,6 +94,10 @@ public interface BpmTaskConvert {
             // 表单信息
             BpmFormDO form = MapUtil.get(formMap, NumberUtils.parseLong(task.getFormKey()), BpmFormDO.class);
             if (form != null) {
+                // 测试一下权限处理
+//                List<String> afterChangedFields = BpmnFormUtils.changeCreateFormFiledPermissionRule(form.getFields(),
+//                        BpmnModelUtils.parseFormFieldsPermission(bpmnModel, task.getTaskDefinitionKey()));
+
                 taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf())
                         .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task));
             }

+ 24 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/SimpleModelConstants.java

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums;
+
+/**
+ * 仿钉钉快搭 JSON 常量信息
+ *
+ * @author jason
+ */
+public interface SimpleModelConstants {
+
+    /**
+     * 流程表单字段权限, 用于标记字段权限
+     */
+    String FIELDS_PERMISSION = "fieldsPermission";
+
+    /**
+     * 字段属性
+     */
+    String FIELD_ATTRIBUTE = "field";
+
+    /**
+     * 权限属性
+     */
+    String PERMISSION_ATTRIBUTE = "permission";
+}

+ 23 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java

@@ -1,9 +1,12 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
+import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants;
 import org.flowable.bpmn.converter.BpmnXMLConverter;
 import org.flowable.bpmn.model.Process;
 import org.flowable.bpmn.model.*;
@@ -11,6 +14,9 @@ import org.flowable.common.engine.impl.util.io.BytesStreamSource;
 
 import java.util.*;
 
+import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.FIELD_ATTRIBUTE;
+import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
+
 /**
  * 流程模型转操作工具类
  */
@@ -38,6 +44,23 @@ public class BpmnModelUtils {
         return candidateParam;
     }
 
+    public static Map<String,Integer> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) {
+        FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
+        if (flowElement == null) {
+            return null;
+        }
+        final HashMap<String, Integer> fieldsPermission = MapUtil.newHashMap();
+        List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(SimpleModelConstants.FIELDS_PERMISSION);
+        extensionElements.forEach(el -> {
+            String field = el.getAttributeValue(FLOWABLE_EXTENSIONS_NAMESPACE, FIELD_ATTRIBUTE);
+            String permission = el.getAttributeValue(FLOWABLE_EXTENSIONS_NAMESPACE, SimpleModelConstants.PERMISSION_ATTRIBUTE);
+            if (StrUtil.isNotEmpty(field) && StrUtil.isNotEmpty(permission)) {
+                fieldsPermission.put(field, Integer.parseInt(permission));
+            }
+        });
+        return fieldsPermission;
+    }
+
     /**
      * 根据节点,获取入口连线
      *

+ 51 - 10
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.lang.Assert;
+import cn.hutool.core.lang.TypeReference;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.StrUtil;
@@ -8,11 +10,13 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimp
 import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
 import org.flowable.bpmn.BpmnAutoLayout;
-import org.flowable.bpmn.model.*;
 import org.flowable.bpmn.model.Process;
+import org.flowable.bpmn.model.*;
 
 import java.util.List;
+import java.util.Map;
 
+import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.FIELDS_PERMISSION;
 import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
 import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
 
@@ -71,7 +75,7 @@ public class SimpleModelUtils {
             case USER_TASK:
             case COPY_TASK:
             case PARALLEL_GATEWAY_JOIN:
-            case INCLUSIVE_GATEWAY_JOIN:{
+            case INCLUSIVE_GATEWAY_JOIN: {
                 SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), childNode.getId(), null, null);
                 mainProcess.addFlowElement(sequenceFlow);
                 // 递归调用后续节点
@@ -80,7 +84,7 @@ public class SimpleModelUtils {
             }
             case PARALLEL_GATEWAY_FORK:
             case EXCLUSIVE_GATEWAY:
-            case INCLUSIVE_GATEWAY_FORK:{
+            case INCLUSIVE_GATEWAY_FORK: {
                 String sequenceFlowTargetId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
                 List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
                 Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
@@ -202,15 +206,20 @@ public class SimpleModelUtils {
         serviceTask.setName(node.getName());
         // TODO @jason:建议使用 ServiceTask,通过 executionListeners 实现;
         // @芋艿 ServiceTask 就可以了吧。 不需要 executionListeners
-        addExtensionElement(node, serviceTask);
+        addCandidateElements(node, serviceTask);
+
         return serviceTask;
     }
 
-    private static void addExtensionElement(BpmSimpleModelNodeVO node, FlowElement flowElement) {
+
+    /**
+     * 给节点添加候选人元素
+     */
+    private static void addCandidateElements(BpmSimpleModelNodeVO node, FlowElement flowElement) {
         Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
-        addExtensionElement(flowElement, FLOWABLE_EXTENSIONS_NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
+        addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
                 candidateStrategy == null ? null : String.valueOf(candidateStrategy));
-        addExtensionElement(flowElement, FLOWABLE_EXTENSIONS_NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
+        addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
                 MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
     }
 
@@ -245,16 +254,48 @@ public class SimpleModelUtils {
         UserTask userTask = new UserTask();
         userTask.setId(node.getId());
         userTask.setName(node.getName());
-        addExtensionElement(node, userTask);
+        // TODO 暂时测试,后面去掉
+        userTask.setFormKey("24");
+        // 添加候选人元素
+        addCandidateElements(node, userTask);
+        // 添加表单字段权限属性元素
+        addFormFieldsPermission(node, userTask);
         return userTask;
     }
 
-    private static void addExtensionElement(FlowElement element, String namespace, String name, String value) {
+    /**
+     * 给节点添加表单字段权限元素
+     */
+    private static void addFormFieldsPermission(BpmSimpleModelNodeVO node, FlowElement flowElement) {
+        List<Map<String, String>> fieldsPermissions = MapUtil.get(node.getAttributes(),
+                FIELDS_PERMISSION, new TypeReference<>() {});
+        if (CollUtil.isNotEmpty(fieldsPermissions)) {
+            fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FIELDS_PERMISSION, item));
+        }
+    }
+
+    private static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {
+        if (attributes == null) {
+            return;
+        }
+        ExtensionElement extensionElement = new ExtensionElement();
+        extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
+        extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
+        extensionElement.setName(name);
+        attributes.forEach((key, value) -> {
+            ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value);
+            extensionAttribute.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
+            extensionElement.addAttribute(extensionAttribute);
+        });
+        element.addExtensionElement(extensionElement);
+    }
+
+    private static void addExtensionElement(FlowElement element, String name, String value) {
         if (value == null) {
             return;
         }
         ExtensionElement extensionElement = new ExtensionElement();
-        extensionElement.setNamespace(namespace);
+        extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
         extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
         extensionElement.setElementText(value);
         extensionElement.setName(name);

+ 68 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/util/BpmnFormUtils.java

@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.module.bpm.service.util;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.module.bpm.enums.definition.BpmFieldPermissionEnum;
+import com.fasterxml.jackson.core.type.TypeReference;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.SimpleModelConstants.FIELD_ATTRIBUTE;
+
+/**
+ *  Bpmn 流程表单相关工具方法
+ *
+ * @author jason
+ */
+public class BpmnFormUtils {
+    private static final String CREATE_FORM_DISPLAY_ATTRIBUTE = "display";
+    private static final String CREATE_FORM_DISABLED_ATTRIBUTE = "disabled";
+
+    /**
+     * 修改 form-create 表单组件字段权限规则: 包括可编辑、只读、隐藏规则
+     * @param fields 字段规则
+     * @param fieldsPermission 字段权限
+     * @return 修改权限后的字段规则
+     */
+    public static List<String> changeCreateFormFiledPermissionRule(List<String> fields, Map<String,Integer> fieldsPermission) {
+        if ( CollUtil.isEmpty(fields)  || MapUtil.isEmpty(fieldsPermission)) {
+            return fields;
+        }
+        List<String> afterChangedFields = new ArrayList<>(fields.size());
+        fields.forEach( f-> {
+            Map<String, Object> fieldMap = JsonUtils.parseObject(f, new TypeReference<>() {});
+            String field = ObjUtil.defaultIfNull(fieldMap.get(FIELD_ATTRIBUTE), Object::toString, "");
+            if (StrUtil.isEmpty(field) || !fieldsPermission.containsKey(field)) {
+                afterChangedFields.add(f);
+                return;
+            }
+            BpmFieldPermissionEnum fieldPermission = BpmFieldPermissionEnum.valueOf(fieldsPermission.get(field));
+            Assert.notNull(fieldPermission, "字段权限不匹配");
+            if (BpmFieldPermissionEnum.HIDE == fieldPermission) {
+                fieldMap.put(CREATE_FORM_DISPLAY_ATTRIBUTE, Boolean.FALSE);
+            } else if (BpmFieldPermissionEnum.EDITABLE == fieldPermission){
+                Map<String, Object> props =  MapUtil.get(fieldMap, "props", new cn.hutool.core.lang.TypeReference<>() {});
+                if (props == null) {
+                    props = MapUtil.newHashMap();
+                    fieldMap.put("props", props);
+                }
+                props.put(CREATE_FORM_DISABLED_ATTRIBUTE, Boolean.FALSE);
+            } else if (BpmFieldPermissionEnum.READONLY == fieldPermission) {
+                Map<String, Object> props =  MapUtil.get(fieldMap, "props", new cn.hutool.core.lang.TypeReference<>() {});
+                if (props == null) {
+                    props = MapUtil.newHashMap();
+                    fieldMap.put("props", props);
+                }
+                props.put(CREATE_FORM_DISABLED_ATTRIBUTE, Boolean.TRUE);
+            }
+            afterChangedFields.add(JsonUtils.toJsonString(fieldMap));
+        });
+        return afterChangedFields;
+    }
+}