diff --git a/src/main/java/neatlogic/module/process/api/channel/ChannelFormGetApi.java b/src/main/java/neatlogic/module/process/api/channel/ChannelFormGetApi.java index 437a2f6163efd7dbc0d0f7927d0f15dd9400ac14..69540415c81d05e2a79805d4e20444975180d86a 100644 --- a/src/main/java/neatlogic/module/process/api/channel/ChannelFormGetApi.java +++ b/src/main/java/neatlogic/module/process/api/channel/ChannelFormGetApi.java @@ -76,9 +76,11 @@ public class ChannelFormGetApi extends PrivateApiComponentBase { return null; } - @Input({@Param(name = "channelUuidList", type = ApiParamType.JSONARRAY, isRequired = true, desc = "服务uuidList"), - @Param(name = "conditionModel", type = ApiParamType.ENUM, rule = "simple,custom", isRequired = true, - desc = "条件模型 simple|custom, simple:目前用于用于工单中心条件过滤简单模式, custom:目前用于用于工单中心条件过自定义模式、条件分流和sla条件;默认custom"),}) + @Input({ + @Param(name = "channelUuidList", type = ApiParamType.JSONARRAY, isRequired = true, desc = "服务uuidList"), + @Param(name = "conditionModel", type = ApiParamType.ENUM, rule = "simple,custom,all", isRequired = true, + desc = "条件模型 simple|custom|all , simple:目前用于用于工单中心条件过滤简单模式, custom:目前用于用于工单中心条件过自定义模式、条件分流和sla条件;默认custom") + }) @Output({@Param(name = "Return", explode = FormAttributeVo[].class, desc = "表单属性列表")}) @Description(desc = "服务绑定的表单属性信息获取接口") @Override @@ -121,7 +123,7 @@ public class ChannelFormGetApi extends PrivateApiComponentBase { formAttributeVo.setHandler(new CheckboxHandler().getHandler()); } IFormAttributeHandler handler = FormAttributeHandlerFactory.getHandler(formAttributeVo.getHandler()); - if (handler == null || !handler.isConditionable()) { + if (handler == null || (!Objects.equals("all", conditionModel) && !handler.isConditionable())) { formIterator.remove(); continue; } diff --git a/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftGetApi.java b/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftGetApi.java index 1d9cc110610b0f300ce64b00397ce85e35fbfc4b..d7246839c830155441dfb39f5d14f4a788b93721 100644 --- a/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftGetApi.java +++ b/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftGetApi.java @@ -16,6 +16,9 @@ package neatlogic.module.process.api.processtask; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.JSONPath; import neatlogic.framework.asynchronization.threadlocal.UserContext; import neatlogic.framework.auth.core.AuthAction; import neatlogic.framework.common.constvalue.ApiParamType; @@ -34,6 +37,7 @@ import neatlogic.framework.process.exception.channeltype.ChannelTypeNotFoundExce import neatlogic.framework.process.exception.operationauth.ProcessTaskPermissionDeniedException; import neatlogic.framework.process.exception.process.ProcessNotFoundException; import neatlogic.framework.process.exception.process.ProcessStepHandlerNotFoundException; +import neatlogic.framework.process.exception.process.ProcessStepUtilHandlerNotFoundException; import neatlogic.framework.process.exception.processtask.ProcessTaskNotFoundEditTargetException; import neatlogic.framework.process.operationauth.core.ProcessAuthManager; import neatlogic.framework.process.stephandler.core.IProcessStepInternalHandler; @@ -44,8 +48,6 @@ import neatlogic.framework.restful.core.privateapi.PrivateApiComponentBase; import neatlogic.module.process.dao.mapper.ProcessMapper; import neatlogic.module.process.service.CatalogService; import neatlogic.module.process.service.ProcessTaskService; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.JSONPath; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; @@ -107,7 +109,8 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { @Param(name = "copyProcessTaskId", type = ApiParamType.LONG, desc = "term.itsm.copyprocesstaskid", help = "从复制上报进入上报页时,传copyProcessTaskId"), @Param(name = "channelUuid", type = ApiParamType.STRING, desc = "term.itsm.channeluuid", help = "从服务目录进入上报页时,传channelUuid"), @Param(name = "fromProcessTaskId", type = ApiParamType.LONG, desc = "term.itsm.fromprocesstaskid", help = "从转报进入上报页时,传fromProcessTaskId"), - @Param(name = "channelTypeRelationId", type = ApiParamType.LONG, desc = "term.itsm.channeltyperelationid", help = "从转报进入上报页时,传channelTypeRelationId") + @Param(name = "channelTypeRelationId", type = ApiParamType.LONG, desc = "term.itsm.channeltyperelationid", help = "从转报进入上报页时,传channelTypeRelationId"), + @Param(name = "parentProcessTaskStepId", type = ApiParamType.LONG, desc = "nmpap.processtaskdraftgetapi.input.param.desc.parentprocesstaskstepid", help = "创建子流程时,传parentProcessTaskStepId") }) @Output({ @Param(explode = ProcessTaskVo.class, desc = "term.itsm.processtaskinfo") @@ -117,6 +120,7 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { public Object myDoService(JSONObject jsonObj) throws Exception { Long processTaskId = jsonObj.getLong("processTaskId"); Long copyProcessTaskId = jsonObj.getLong("copyProcessTaskId"); + Long parentProcessTaskStepId = jsonObj.getLong("parentProcessTaskStepId"); String channelUuid = jsonObj.getString("channelUuid"); ProcessTaskVo processTaskVo = null; if (processTaskId != null) { @@ -131,11 +135,11 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { } catch (ProcessTaskPermissionDeniedException e) { throw new PermissionDeniedException(e.getMessage()); } - processTaskVo= getProcessTaskVoByProcessTaskId(processTaskId); + processTaskVo = getProcessTaskVoByProcessTaskId(processTaskId); } else if (copyProcessTaskId != null) { //复制上报 if (processTaskMapper.getProcessTaskBaseInfoById(copyProcessTaskId) == null) { - throw new ProcessTaskNotFoundEditTargetException(copyProcessTaskId); + throw new ProcessTaskNotFoundEditTargetException(copyProcessTaskId); } try { new ProcessAuthManager.TaskOperationChecker(copyProcessTaskId, ProcessTaskOperationType.PROCESSTASK_COPYPROCESSTASK) @@ -144,11 +148,15 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { } catch (ProcessTaskPermissionDeniedException e) { throw new PermissionDeniedException(e.getMessage()); } - processTaskVo = getProcessTaskVoByCopyProcessTaskId(copyProcessTaskId); + processTaskVo = getProcessTaskVoByCopyProcessTaskId(copyProcessTaskId); } else if (channelUuid != null) { if (channelMapper.checkChannelIsExists(channelUuid) == 0) { throw new ChannelNotFoundEditTargetException(channelUuid); } + /* 判断当前用户是否拥有channelUuid服务的上报权限 */ + if (!catalogService.channelIsAuthority(channelUuid, UserContext.get().getUserUuid(true))) { + throw new PermissionDeniedException(); + } Long channelTypeRelationId = jsonObj.getLong("channelTypeRelationId"); Long fromProcessTaskId = jsonObj.getLong("fromProcessTaskId"); if (fromProcessTaskId != null) { @@ -169,30 +177,58 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { // if (!flag) { // new ProcessTaskOperationUnauthorizedException(ProcessTaskOperationType.PROCESSTASK_TRANSFERREPORT); // } + } else if ( parentProcessTaskStepId != null) { + return getParentProcessTask( parentProcessTaskStepId, channelUuid); } - /** 判断当前用户是否拥有channelUuid服务的上报权限 **/ - if (!catalogService.channelIsAuthority(channelUuid, UserContext.get().getUserUuid(true))) { - throw new PermissionDeniedException(); - } - processTaskVo = getProcessTaskVoByChannelUuid(channelUuid, fromProcessTaskId, channelTypeRelationId); + processTaskVo = getProcessTaskVoByChannelUuid(channelUuid, fromProcessTaskId, channelTypeRelationId); } else { - throw new ParamNotExistsException("processTaskId", "copyProcessTaskId", "channelUuid"); + throw new ParamNotExistsException("processTaskId", "copyProcessTaskId", "channelUuid", "parentProcessTaskStepId"); } return processTaskVo; } /** - * @Description: 获取来源工单中与当前工单表单属性标签名相同的属性值 - * @Author: linbq - * @Date: 2021/1/27 15:26 - * @Params:[fromProcessTaskId, toProcessTaskFormConfig] - * @Returns:java.util.Map + * 获取来源工单中与当前工单表单属性标签名相同的属性值 + * + * @param fromProcessTaskId 来源工单id + * @param toProcessTaskFormConfig 目标工单表单配置 **/ - private Map getFromFormAttributeDataMap(Long fromProcessTaskId, JSONObject toProcessTaskFormConfig){ + private Map getFromFormAttributeDataMap(Long fromProcessTaskId, JSONObject toProcessTaskFormConfig) { Map resultObj = new HashMap<>(); if (MapUtils.isEmpty(toProcessTaskFormConfig)) { return resultObj; } + JSONObject fromProcessTaskFormAttrDataMap = getFromFormAttributeDataMap(fromProcessTaskId); + JSONObject labelUuidMap = fromProcessTaskFormAttrDataMap.getJSONObject("labelUuidMap"); + JSONObject labelHandlerMap = fromProcessTaskFormAttrDataMap.getJSONObject("labelHandlerMap"); + JSONObject formAttributeDataMap = fromProcessTaskFormAttrDataMap.getJSONObject("formAttributeDataMap"); + //获取目标表单值 + FormVersionVo toFormVersion = new FormVersionVo(); + toFormVersion.setFormConfig(toProcessTaskFormConfig); + for (FormAttributeVo formAttributeVo : toFormVersion.getFormAttributeList()) { + String fromFormAttributeHandler = labelHandlerMap.getString(formAttributeVo.getLabel()); + if (Objects.equals(fromFormAttributeHandler, formAttributeVo.getHandler())) { + String fromFormAttributeUuid = labelUuidMap.getString(formAttributeVo.getLabel()); + if (StringUtils.isNotBlank(fromFormAttributeUuid)) { + Object data = formAttributeDataMap.get(fromFormAttributeUuid); + if (data != null) { + resultObj.put(formAttributeVo.getUuid(), data); + } + } + } + } + + + return resultObj; + } + + /** + * 获取来源工单的表单值映射 + * + * @param fromProcessTaskId 来源工单id + */ + private JSONObject getFromFormAttributeDataMap(Long fromProcessTaskId) { + JSONObject resultObj = new JSONObject(); // 获取旧工单表单信息 ProcessTaskFormVo processTaskFormVo = processTaskMapper.getProcessTaskFormByProcessTaskId(fromProcessTaskId); if (processTaskFormVo != null && StringUtils.isNotBlank(processTaskFormVo.getFormContentHash())) { @@ -201,7 +237,7 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { Map labelUuidMap = new HashMap<>(); Map labelHandlerMap = new HashMap<>(); FormVersionVo fromFormVersion = new FormVersionVo(); - fromFormVersion.setFormConfig(JSONObject.parseObject(formContent)); + fromFormVersion.setFormConfig(JSON.parseObject(formContent)); List fromFormAttributeList = fromFormVersion.getFormAttributeList(); for (FormAttributeVo formAttributeVo : fromFormAttributeList) { labelUuidMap.put(formAttributeVo.getLabel(), formAttributeVo.getUuid()); @@ -212,21 +248,9 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { for (ProcessTaskFormAttributeDataVo processTaskFormAttributeDataVo : processTaskFormAttributeDataList) { formAttributeDataMap.put(processTaskFormAttributeDataVo.getAttributeUuid(), processTaskFormAttributeDataVo.getDataObj()); } - - FormVersionVo toFormVersion = new FormVersionVo(); - toFormVersion.setFormConfig(toProcessTaskFormConfig); - for (FormAttributeVo formAttributeVo : toFormVersion.getFormAttributeList()) { - String fromFormAttributeHandler = labelHandlerMap.get(formAttributeVo.getLabel()); - if (Objects.equals(fromFormAttributeHandler, formAttributeVo.getHandler())) { - String fromFormAttributeUuid = labelUuidMap.get(formAttributeVo.getLabel()); - if (StringUtils.isNotBlank(fromFormAttributeUuid)) { - Object data = formAttributeDataMap.get(fromFormAttributeUuid); - if (data != null) { - resultObj.put(formAttributeVo.getUuid(), data); - } - } - } - } + resultObj.put("labelUuidMap", labelUuidMap); + resultObj.put("labelHandlerMap", labelHandlerMap); + resultObj.put("formAttributeDataMap", formAttributeDataMap); } } return resultObj; @@ -247,13 +271,65 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { return processTaskVo; } + /** + * 获取子流程工单信息(包含父流程的表单值) + * + * @param parentProcessTaskStepId 父工单步骤id + * @param channelUuid 子流程服务uuid + */ + private ProcessTaskVo getParentProcessTask(Long parentProcessTaskStepId, String channelUuid) throws Exception { + ProcessTaskStepVo parentProcessTaskStepVo = processTaskMapper.getProcessTaskStepBaseInfoById(parentProcessTaskStepId); + processTaskService.checkProcessTaskParamsIsLegal(parentProcessTaskStepVo.getProcessTaskId(), parentProcessTaskStepId); + ProcessTaskVo processTaskVo = getProcessTaskVoByChannelUuid(channelUuid); + JSONObject fromProcessTaskFormAttrDataMap = getFromFormAttributeDataMap(parentProcessTaskStepVo.getProcessTaskId()); + JSONObject labelUuidMap = fromProcessTaskFormAttrDataMap.getJSONObject("labelUuidMap"); + JSONObject labelHandlerMap = fromProcessTaskFormAttrDataMap.getJSONObject("labelHandlerMap"); + JSONObject formAttributeDataMap = fromProcessTaskFormAttrDataMap.getJSONObject("formAttributeDataMap"); + //获取父流程步骤配置信息 + IProcessStepInternalHandler processStepUtilHandler = ProcessStepInternalHandlerFactory.getHandler(parentProcessTaskStepVo.getHandler()); + if (processStepUtilHandler == null) { + throw new ProcessStepUtilHandlerNotFoundException(parentProcessTaskStepVo.getHandler()); + } + Object parenStepInfoObj = processStepUtilHandler.getHandlerStepInitInfo(parentProcessTaskStepVo); + if (parenStepInfoObj != null) { + JSONObject parenStepInfo = (JSONObject) parenStepInfoObj; + JSONObject parentStepChannelFormMapping = parenStepInfo.getJSONObject("formMapping"); + if (MapUtils.isNotEmpty(parentStepChannelFormMapping)) { + JSONObject parentSubProcessTaskStepConfigFormMapping = parentStepChannelFormMapping.getJSONObject(channelUuid); + if (MapUtils.isNotEmpty(parentSubProcessTaskStepConfigFormMapping)) { + //获取目标表单值 + Map resultObj = new HashMap<>(); + FormVersionVo toFormVersion = new FormVersionVo(); + toFormVersion.setFormConfig(processTaskVo.getFormConfig()); + for (FormAttributeVo formAttributeVo : toFormVersion.getFormAttributeList()) { + String parentFormLabel = parentSubProcessTaskStepConfigFormMapping.getString(formAttributeVo.getLabel()); + if (StringUtils.isNotBlank(parentFormLabel)) { + String fromFormAttributeHandler = labelHandlerMap.getString(parentFormLabel); + if (Objects.equals(fromFormAttributeHandler, formAttributeVo.getHandler())) { + String fromFormAttributeUuid = labelUuidMap.getString(parentFormLabel); + if (StringUtils.isNotBlank(fromFormAttributeUuid)) { + Object data = formAttributeDataMap.get(fromFormAttributeUuid); + if (data != null) { + resultObj.put(formAttributeVo.getUuid(), data); + } + } + } + } + } + processTaskVo.setFormAttributeDataMap(resultObj); + } + } + } + return processTaskVo; + } + private ProcessTaskVo getProcessTaskVoByCopyProcessTaskId(Long copyProcessTaskId) throws Exception { ProcessTaskVo oldProcessTaskVo = processTaskService.checkProcessTaskParamsIsLegal(copyProcessTaskId); ProcessTaskVo processTaskVo = getProcessTaskVoByChannelUuid(oldProcessTaskVo.getChannelUuid(), null, null); processTaskVo.setTitle(oldProcessTaskVo.getTitle()); List channelPriorityList = channelMapper.getChannelPriorityListByChannelUuid(oldProcessTaskVo.getChannelUuid()); - if(CollectionUtils.isNotEmpty(channelPriorityList)) { + if (CollectionUtils.isNotEmpty(channelPriorityList)) { processTaskVo.setIsNeedPriority(1); for (ChannelPriorityVo channelPriority : channelPriorityList) { if (oldProcessTaskVo.getPriorityUuid().equals(channelPriority.getPriorityUuid())) { @@ -261,7 +337,7 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { break; } } - }else{ + } else { processTaskVo.setIsNeedPriority(0); } @@ -300,7 +376,11 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { return processTaskVo; } - private ProcessTaskVo getProcessTaskVoByChannelUuid(String channelUuid, Long fromProcessTaskId, Long channelTypeRelationId) throws PermissionDeniedException { + private ProcessTaskVo getProcessTaskVoByChannelUuid(String channelUuid) { + return getProcessTaskVoByChannelUuid(channelUuid, null, null); + } + + private ProcessTaskVo getProcessTaskVoByChannelUuid(String channelUuid, Long fromProcessTaskId, Long channelTypeRelationId) { ChannelVo channel = channelMapper.getChannelByUuid(channelUuid); if (channel == null) { throw new ChannelNotFoundException(channelUuid); @@ -335,14 +415,14 @@ public class ProcessTaskDraftGetApi extends PrivateApiComponentBase { String worktimeUuid = channelMapper.getWorktimeUuidByChannelUuid(channelUuid); processTaskVo.setWorktimeUuid(worktimeUuid); List channelPriorityList = channelMapper.getChannelPriorityListByChannelUuid(channelUuid); - if(CollectionUtils.isNotEmpty(channelPriorityList)) { + if (CollectionUtils.isNotEmpty(channelPriorityList)) { processTaskVo.setIsNeedPriority(1); for (ChannelPriorityVo channelPriority : channelPriorityList) { if (channelPriority.getIsDefault().intValue() == 1) { processTaskVo.setPriorityUuid(channelPriority.getPriorityUuid()); } } - }else{ + } else { processTaskVo.setIsNeedPriority(0); } diff --git a/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftSaveApi.java b/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftSaveApi.java index b4a2a324140db26c54021d28ecbed15b8546b8f6..313158b50850e7c12d22cd07b0f263c991d75ee9 100644 --- a/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftSaveApi.java +++ b/src/main/java/neatlogic/module/process/api/processtask/ProcessTaskDraftSaveApi.java @@ -77,7 +77,9 @@ public class ProcessTaskDraftSaveApi extends PrivateApiComponentBase { @Param(name = "handlerStepInfo", type = ApiParamType.JSONOBJECT, desc = "处理器特有的步骤信息"), @Param(name = "fromProcessTaskId", type = ApiParamType.LONG, desc = "来源工单id,从转报进入上报页时,传fromProcessTaskId"), @Param(name = "channelTypeRelationId", type = ApiParamType.LONG, desc = "关系类型id,从转报进入上报页时,传channelTypeRelationId"), - @Param(name = "source", type = ApiParamType.STRING, defaultValue = "pc", desc = "来源") + @Param(name = "source", type = ApiParamType.STRING, defaultValue = "pc", desc = "来源"), + @Param(name = "parentProcessTaskStepId", type = ApiParamType.LONG, desc = "nmpap.processtaskdraftgetapi.input.param.desc.parentprocesstaskstepid", help = "创建子流程时,传parentProcessTaskStepId"), + @Param(name = "invoke", type = ApiParamType.STRING, desc = "nmpap.processtaskdraftsaveapi.input.param.desc.invoke", help = "subprocess :子流程") }) @Output({ @Param(name = "processTaskId", type = ApiParamType.LONG, desc = "工单id"), diff --git a/src/main/java/neatlogic/module/process/dao/mapper/ProcessTaskMapper.xml b/src/main/java/neatlogic/module/process/dao/mapper/ProcessTaskDataMapper.xml similarity index 100% rename from src/main/java/neatlogic/module/process/dao/mapper/ProcessTaskMapper.xml rename to src/main/java/neatlogic/module/process/dao/mapper/ProcessTaskDataMapper.xml diff --git a/src/main/java/neatlogic/module/process/thread/ProcessTaskNotifyThread.java b/src/main/java/neatlogic/module/process/thread/ProcessTaskNotifyThread.java index 11a9f9db6ac8d17bc237f8c85ea116b4aeace74c..15049d2efa8ea8edfc85520de270447bb5e2461d 100644 --- a/src/main/java/neatlogic/module/process/thread/ProcessTaskNotifyThread.java +++ b/src/main/java/neatlogic/module/process/thread/ProcessTaskNotifyThread.java @@ -103,6 +103,10 @@ public class ProcessTaskNotifyThread extends NeatLogicThread { notifyAuditMessageStringBuilder.append(currentProcessTaskStepVo.getProcessTaskId()); } else { /* 获取步骤配置信息 **/ + //如果有父工单步骤,则使用父工单步骤的配置策略,兼容子流程场景 + if(currentProcessTaskStepVo.getParentProcessTaskStepId() != null){ + + } ProcessTaskStepVo stepVo = processTaskMapper.getProcessTaskStepBaseInfoById(currentProcessTaskStepVo.getId()); IProcessStepInternalHandler processStepUtilHandler = ProcessStepInternalHandlerFactory.getHandler(stepVo.getHandler()); if (processStepUtilHandler == null) { diff --git a/src/main/resources/neatlogic/resources/process/changelog/2024-03-18/neatlogic_tenant.sql b/src/main/resources/neatlogic/resources/process/changelog/2024-03-18/neatlogic_tenant.sql new file mode 100644 index 0000000000000000000000000000000000000000..4a492b02abac93e6aa7640ed18bf1b8713aab319 --- /dev/null +++ b/src/main/resources/neatlogic/resources/process/changelog/2024-03-18/neatlogic_tenant.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS `processtask_invoke` ( + `processtask_id` bigint NOT NULL COMMENT '工单步骤id', + `source` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '来源', + `type` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '来源类型', + `invoke_id` bigint DEFAULT NULL COMMENT '来源id', + PRIMARY KEY (`processtask_id`), + KEY `idx_invokeid` (`invoke_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='工单来源'; \ No newline at end of file diff --git a/src/main/resources/neatlogic/resources/process/changelog/2024-03-18/version.json b/src/main/resources/neatlogic/resources/process/changelog/2024-03-18/version.json new file mode 100644 index 0000000000000000000000000000000000000000..cb0b4e701aaca308b40658b43d2ec8f764a8fd80 --- /dev/null +++ b/src/main/resources/neatlogic/resources/process/changelog/2024-03-18/version.json @@ -0,0 +1,10 @@ +{ + "content":[ + { + "type":"新增功能", + "detail":[ + {"msg":"1.父子流程"} + ] + } + ] +}