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

This commit is contained in:
jason 2024-04-14 10:07:55 +08:00
parent 9456d461f9
commit 1e30e4851a
7 changed files with 206 additions and 12 deletions

View File

@ -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());
}
}

View File

@ -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")

View File

@ -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));
}

View File

@ -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";
}

View File

@ -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;
}
/**
* 根据节点获取入口连线
*

View File

@ -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);

View File

@ -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;
}
}