diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index ca4bcb3..b815780 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java @@ -44,6 +44,7 @@ public interface ErrorCodeConstants { ErrorCode PROCESS_INSTANCE_HTTP_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 请求调用失败"); ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); + ErrorCode PROCESS_INSTANCE_START_FORM_FIELD_REQUIRED = new ErrorCode(1_009_004_009, "发起流程失败,表单字段({})为必填项,不能为空"); // ========== 流程任务 1-009-005-000 ========== ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index a1050ab..d55d3df 100644 --- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -22,6 +22,7 @@ import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.dal.redis.BpmProcessIdRedisDAO; import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmAttachmentTypeEnum; @@ -763,11 +764,54 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 获得流程定义 ProcessDefinition definition = processDefinitionService .getProcessDefinition(createReqVO.getProcessDefinitionId()); + // 校验动态表单必填字段(前端校验可被接口直调绕过,后端必须兜底,否则必填缺失也会建成脏实例) + validateDynamicFormRequiredFields(definition, createReqVO.getVariables()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } + /** + * 校验动态表单(formType=10 流程表单)的必填字段 + * + * 注意:仅校验接口发起(VO 路径)入口。内部业务链路(DTO 路径,例如 OA 请假) + * 由业务代码自行组装变量,不经过本校验,避免误伤。 + */ + private void validateDynamicFormRequiredFields(ProcessDefinition definition, Map variables) { + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(definition.getId()); + if (processDefinitionInfo == null + || !BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType()) + || CollUtil.isEmpty(processDefinitionInfo.getFormFields())) { + return; + } + List fieldStrs = processDefinitionInfo.getFormFields(); + // formFields 是 JSON 数组,每个元素本身又是 JSON 字符串;两种表单设计器并存: + // 字段标识可能是 field 或 key,必填标记可能是数组 ["required"] 或对象 {"required": true} + for (String fieldStr : fieldStrs) { + Map field = JsonUtils.parseMap(fieldStr); + boolean required = false; + Object validate = field.get("validate"); + if (validate instanceof List) { + required = ((List) validate).contains("required"); + } else if (validate instanceof Map) { + required = Boolean.TRUE.equals(((Map) validate).get("required")); + } + if (!required) { + continue; + } + String fieldId = Convert.toStr(field.get("field"), Convert.toStr(field.get("key"))); + if (StrUtil.isEmpty(fieldId)) { + continue; + } + Object value = variables == null ? null : variables.get(fieldId); + if (value == null || (value instanceof String && StrUtil.isBlank((String) value))) { + throw exception(PROCESS_INSTANCE_START_FORM_FIELD_REQUIRED, + Convert.toStr(field.get("title"), fieldId)); + } + } + } + @Override @DataPermission(enable = false) // 关闭数据权限,避免查询不到用户数据。相关案例:https://gitee.com/zhijiantianya/yudao-cloud/issues/ID1UYA public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) {