OneCode开源低代码引擎技术揭秘(低代码 开源)
《OneCode开源低代码引擎白皮书》部分对于OneCode功能及组成做了详细的描述。本文根据BeeCode团队开放的内测版实测,整理了技术揭秘系列,从产品的设计思想及技术原理方面来阐述以及平台很对模型的赋能能力。本文主要针对的读者是软件专业领域的产品经理、项目管理者、架构师、程序员,如果您第一次阅读本文需要优先阅读 《OneCode开源低代码引擎白皮书》以方便对本文的理解。
一,OneCode 顶层设计
OneCode由三块自成体系的可独立部署运行的部分组成。前端引擎负责界面建模并按低代码协议协议生成标准JSON,中后台OneCode通过读取标准JSON协议,完成后端的视图建模,合并DSM后端服务建模系统,完成完整的后端服务建模应用,通过代码工程完成前后端一体的出码应用。JDSCloud是OneCode的协同支撑系统,除了常规的资源代码空间管理外,提供了独立的沙箱运行环境。为OneCode 提供工程化的仿真版本Ops等服务。
二,OneCode 建模过程
(1) 建模原理
用户通过,拖拽完成页面建模序列化为按标准协议序列化JSON文件,后端OneCode服务支撑系统解析JSON文件并混合DSM建模信息以及后端服务逻辑后,通过混合编译,通过代码工厂指定出码模板,完成前后端一体的编译文件。
(1)拖拽建模
用户由前端低代码引擎提供拖拽支持,将用户需求转换为相应的组件组合,完成建模后根据标准的低代码组件序列化为JSON文件。
低代码设计器组件库
标准组件协议描述方式
(2)后端融合
后端融合系统,通过读取标准JSON组件,完成组件在后端的模型转换。用户可以通过,DSM模型工具或者通过OneCode提供的建模通讯SDK,针对前端需要的服务调用逻辑以及业务数据逻辑,进行建模干预。
(3)混合编译出码
完成后端建模的融合后,通过代码工厂通过出码配置输出为OneCode源码。
三,OneCode编译原理
OneCode 本身基于JAVA语言体系,是在Java Spring 注解基础上的一套扩展子集,混合编译引擎器通过扩展注解构建完整的Domain模型,通过读取标准Spring 注解完成普通Web数据交付及调度过程,通过Domin域模型动态渲染JS文件输出为JSON交付给前端引擎构建页面。
编译原理
转换关系
表单实现
四,平台赋能
(1)标准化能力
OneCode平台在设计上将前端组件的设计上即采用了开放式设计模型及及存储通讯标准。这将在很大程度上为各家低代码平台组件互联互通提供便利,在设计上实现通用通行。避免形成应用孤岛。解决用户被绑定在特定平台的忧虑。
(2)混合编译赋能
OneCode除了在前端实现了标准化组件定义外,还额外提供了后端建模的工具DSM,并通过领域模型将二者打通。这样在前端组件建模时便可以直接调用后端服务模型完成数据部分API构建。而DSM模型工具也可以在后端建模时直接读取前端组件属性,打通前端动作与后端服务的通讯能力。
视图设计器通过,后端模型绑定插件快速选定后端Agg聚合服务模型接口,配置页面快速绑定前后台交互
后端DSM建模通过视图模型扩展直接修改操作,前端组件模型
二者模型的打通大幅降低了低代码平台的劳动强度,同时通过针对模型的建模干预API,在模型建立重构的方面可以从更深的层次上建立业务模型的构建能力。
(3)混编检测(预编译)
传统低代码平台基本上都是完全建立在JS的模型下,在初期建模时结构还算清晰但经过稍有点复杂的逻辑,构建时代码的冗余度以及结构就会变得混乱,特别是页跨页面操作或者完成前后台数据交互时。由于其脚本语言的特点无法完成实时校验,只能运行期测试才能发现问题。采用低代码构建的页面往往只是由于页面中做了一些简单的组件增删或者属性样式就该就会造成不可预期的结果,这大大降低了代码的可维护度。OneCode所构建的领域模型则很好的解决了这一问题,在前后端任意模型发生变化时即可调用混合编译,将页面间的连接关系以及前后台的数据关系进行校验通知。在预编译中提升整体的编译能力。
(4)传统低代码服务赋能
在低代码平台有几个领域是有很不错的实施效果的我们以最常见的表单流程模式来谈一下OneCode赋能
流程 表单为核心应用的BPM模式
用户通过图形界面绘制表单,然后通过自动生成数据库或者绑定特定数据库字段完成数据,再配合流程引擎完成数据流转。传统方案的优势在于其通过简化数据模型即后台提升了快速定制的能力。但其劣势也很明显,但流程数据只能自成体系的完成运转,一旦需要与外部系统交互只能通过代码的二次开发来完成。而表单中如果需要嵌入外部数据源也必须要通过二次开发来处理。这就很大程度限制了其发展。
如果采用OneCode 可以在很大程度上可以缓和这个矛盾。采用OneCode模型后表单的后端模型能力将得到大幅提升,在流程定制过程中可以通过OneCode建模插件直接调用表单业务模型,动态修改表单组件属性,实现更高级别更细力度的展现控制。同时OneCode表单在建模时也可以同步调用流程模型,实时根据流转逻辑完成相应相应。
流程定义干预示例
通过DSM模型后建立表单干预模型
表单干预事例
OneCode列表DSM建模模型
OneCode 流转菜单建模
列表建模OneCode源码示例
@Controller@RequestMapping("/bpm/list/")@MethodChinaName(cname = "工作流列表", imageClass = "bpmfont bpmgongzuoliuzhutiguizeweihuguanli")@BpmDomain(type = BpmDomainType.worklist)@Aggregation(type = AggregationType.customDomain)@TabsAnnotation(closeBtn = true)@TreeAnnotationpublic class WorkListService { @CustomAnnotation(uid = true, hidden = true) String projectId; @MethodChinaName(cname = "流程定义列表") @RequestMapping(method = RequestMethod.POST, value = "ProcessDefVersionList") @GridViewAnnotation(editorPath = "NewProcess") @ModuleAnnotation(imageClass = "bpmfont bpmgongzuoliuzhutiguizeweihuguanli", caption = "流程定义列表") public @ResponseBody ListResultModel<List<ProcessDefVersionView>> getProcessDefVersionList(String projectId) { ListResultModel result = new ListResultModel(); try { BPMCondition condition = new BPMCondition(BPMConditionKey.PROCESSDEF_VERSION_PUBLICATIONSTATUS, Operator.EQUALS, ProcessDefVersionStatus.RELEASED.getType()); BPMCondition sysCondition = new BPMCondition(BPMConditionKey.PROCESSDEF_VERSION_PROCESSDEF_ID, Operator.IN, "SELECT PROCESSDEF_ID FROM BPM_PROCESSDEF WHERE SYSTEMCODE='" ESDFacrory.getESDClient().getSystemCode() "'"); condition.addCondition(sysCondition, JoinOperator.JOIN_AND); if (projectId != null && !projectId.equals("")) { Project project = ESDFacrory.getESDClient().getProjectById(projectId); BPMCondition projectCondition = new BPMCondition(BPMConditionKey.PROCESSDEF_VERSION_PROCESSDEF_ID, Operator.IN, "SELECT PROCESSDEF_ID FROM BPM_PROCESSDEF WHERE CLASSIFICATION='" project.getProjectName() "'"); condition.addCondition(projectCondition, JoinOperator.JOIN_AND); } ListResultModel<List<ProcessDefVersion>> versionList = getClient().getProcessDefVersionList(condition, null, null); result = PageUtil.changPageList(versionList, ProcessDefVersionView.class); } catch (Exception e) { result = new ErrorListResultModel(); ((ErrorListResultModel<List<ProcessInstListView>>) result).setErrdes(e.getMessage()); e.printStackTrace(); } return result; } @MethodChinaName(cname = "草稿箱") @RequestMapping(method = RequestMethod.POST, value = "NotStartActivityInstList") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmgongzuoliu", caption = "草稿箱") public @ResponseBody ListResultModel<List<ActivityInstListView>> getNotStartActivityInstList(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); data.setConditionEnums(RightConditionEnums.CONDITION_CURRENTWORK_NOTSTART); result = searchActivityInst(data); return result; } @MethodChinaName(cname = "最新待办") @RequestMapping(method = RequestMethod.POST, value = "WaitedActivityInstList") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmyuxiandengjibanli", caption = "最新待办") public @ResponseBody ListResultModel<List<ActivityInstListView>> getWaitedActivityInstList(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); data.setConditionEnums(RightConditionEnums.CONDITION_WAITEDWORK); result = searchActivityInst(data); return result; } @MethodChinaName(cname = "所有在办") @RequestMapping(method = RequestMethod.POST, value = "MyActivityInstList") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmdingzhigongzuoliu", caption = "所有在办") public @ResponseBody ListResultModel<List<ActivityInstListView>> getMyActivityInstList(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); data.setConditionEnums(RightConditionEnums.CONDITION_MYWORKNOTREAD); result = searchActivityInst(data); return result; } @MethodChinaName(cname = "已办理") @RequestMapping(method = RequestMethod.POST, value = "CompletedActivityInstList") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmshujukaifagongzuoliushujutansuozuijindakai", caption = "已办理") public @ResponseBody ListResultModel<List<ActivityInstListView>> getCompletedActivityInstList(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); data.setConditionEnums(RightConditionEnums.CONDITION_COMPLETEDWORK); result = searchActivityInst(data); return result; } @MethodChinaName(cname = "归档文件") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmlishihistory2", caption = "归档文件") @RequestMapping(method = RequestMethod.POST, value = "CompletedProcessInstList") public @ResponseBody ListResultModel<List<ProcessInstListView>> getCompletedProcessInstList(@RequestBody SearchData data) { ListResultModel<List<ProcessInstListView>> result = new ListResultModel<List<ProcessInstListView>>(); try { BPMCondition condition = new BPMCondition(BPMConditionKey.PROCESSINST_STATE, Operator.EQUALS, ProcessInstStatus.completed); ListResultModel<List<ProcessInst>> processList = getClient().getProcessInstList(condition, RightConditionEnums.CONDITION_COMPLETEDWORK, null, null); result = PageUtil.changPageList(processList, ProcessInstListView.class); } catch (BPMException e) { result = new ErrorListResultModel(); ((ErrorListResultModel<List<ProcessInstListView>>) result).setErrdes(e.getMessage()); ((ErrorListResultModel<List<ProcessInstListView>>) result).setErrcode(e.getErrorCode()); e.printStackTrace(); } return result; } @MethodChinaName(cname = "阅办工作实例") @RequestMapping(method = RequestMethod.POST, value = "ReadActivityInstList") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmyuedu", caption = "阅办工作实例") public @ResponseBody ListResultModel<List<ActivityInstListView>> getReadActivityInstList(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); data.setConditionEnums(RightConditionEnums.CONDITION_READ); result = searchActivityInst(data); return result; } @MethodChinaName(cname = "阅闭工作实例") @RequestMapping(method = RequestMethod.POST, value = "EndReadActivityInstList") @CustomAnnotation(imageClass = "bpmfont bpmshouhui") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmshouhui", caption = "阅闭工作实例") public @ResponseBody ListResultModel<List<ActivityInstListView>> getEndReadActivityInstList(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); data.setConditionEnums(RightConditionEnums.CONDITION_ENDREAD); result = searchActivityInst(data); return result; } @MethodChinaName(cname = "条件检索") @RequestMapping(method = RequestMethod.POST, value = "searchActivityInst") public @ResponseBody ListResultModel<List<ActivityInstListView>> searchActivityInst(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); BPMCondition condition = null; RightConditionEnums conditionEnums = data.getConditionEnums(); String processDefId = data.getProcessDefId(); if (conditionEnums == null) { conditionEnums = RightConditionEnums.CONDITION_WAITEDWORK; } switch (conditionEnums) { case CONDITION_WAITEDWORK: condition = new BPMCondition(BPMConditionKey.ACTIVITYINST_STATE, Operator.EQUALS, ActivityInstStatus.notStarted); break; case CONDITION_CURRENTWORK_NOTSTART: condition = new BPMCondition(BPMConditionKey.ACTIVITYINST_RUNSTATUS, Operator.EQUALS, ActivityInstRunStatus.PROCESSNOTSTARTED); break; case CONDITION_CURRENTWORK: condition = new BPMCondition(BPMConditionKey.ACTIVITYINST_STATE, Operator.EQUALS, ActivityInstStatus.running); break; case CONDITION_READ: condition = new BPMCondition(BPMConditionKey.ACTIVITYINST_STATE, Operator.EQUALS, ActivityInstStatus.READ); break; case CONDITION_ENDREAD: condition = new BPMCondition(BPMConditionKey.ACTIVITYINST_STATE, Operator.EQUALS, ActivityInstStatus.ENDREAD); break; default: condition = new BPMCondition(BPMConditionKey.ACTIVITYINST_STATE, Operator.NOT_EQUAL, ActivityInstStatus.aborted); break; } if (processDefId != null) { BPMCondition ccondition = new BPMCondition(BPMConditionKey.ACTIVITYINST_PROCESSDEF_ID, Operator.EQUALS, processDefId); condition.addCondition(ccondition, JoinOperator.JOIN_AND); } if (data.getStartTime() != null && data.getStartTime() != 0) { BPMCondition startcondition = new BPMCondition(BPMConditionKey.ACTIVITYINST_ARRIVEDTIME, Operator.GREATER_THAN, data.getStartTime()); condition.addCondition(startcondition, JoinOperator.JOIN_AND); } if (data.getEndTime() != null && data.getEndTime() != 0) { BPMCondition endcondition = new BPMCondition(BPMConditionKey.ACTIVITYINST_ARRIVEDTIME, Operator.LESS_THAN, data.getEndTime()); condition.addCondition(endcondition, JoinOperator.JOIN_AND); } if (data.getTitle() != null && !data.getTitle().equals("")) { BPMCondition endcondition = new BPMCondition(BPMConditionKey.ACTIVITYINST_PROCESSINST_ID, Operator.IN, "select PROCESSINST_ID from BPM_PROCESSINSTANCE where " BPMConditionKey.PROCESSINST_NAME " like '%" data.getTitle() "%'"); condition.addCondition(endcondition, JoinOperator.JOIN_AND); } String projectId = data.getProjectId(); try { if (projectId != null && !projectId.equals("")) { Project project = ESDFacrory.getESDClient().getProjectById(projectId); BPMCondition projectCondition = new BPMCondition(BPMConditionKey.ACTIVITYINST_PROCESSDEF_ID, Operator.IN, "SELECT PROCESSDEF_ID FROM BPM_PROCESSDEF WHERE CLASSIFICATION='" project.getProjectName() "'"); condition.addCondition(projectCondition, JoinOperator.JOIN_AND); } else { BPMCondition sysCondition = new BPMCondition(BPMConditionKey.ACTIVITYINST_PROCESSDEF_ID, Operator.IN, "SELECT PROCESSDEF_ID FROM BPM_PROCESSDEF WHERE SYSTEMCODE='" ESDFacrory.getESDClient().getSystemCode() "'"); condition.addCondition(sysCondition, JoinOperator.JOIN_AND); } } catch (JDSException e) { e.printStackTrace(); } condition.addOrderBy(new Order(BPMConditionKey.ACTIVITYINST_ARRIVEDTIME, false)); try { ListResultModel<List<ActivityInst>> activityInstList = getClient().getActivityInstList(condition, conditionEnums, null, null); result = PageUtil.changPageList(activityInstList, ActivityInstListView.class); } catch (BPMException e) { result = new ErrorListResultModel(); ((ErrorListResultModel<List<ActivityInstListView>>) result).setErrdes(e.getMessage()); ((ErrorListResultModel<List<ActivityInstListView>>) result).setErrcode(e.getErrorCode()); e.printStackTrace(); } return result; } @MethodChinaName(cname = "条件检索") @RequestMapping(method = RequestMethod.POST, value = "searchActivityHistory") @GridViewAnnotation public @ResponseBody ListResultModel<List<ActivityInstListView>> searchActivityHistory(@RequestBody SearchData data) { ListResultModel<List<ActivityInstListView>> result = new ListResultModel<List<ActivityInstListView>>(); BPMCondition condition = null; RightConditionEnums conditionEnums = data.getConditionEnums(); String processDefId = data.getProcessDefId(); if (conditionEnums == null) { conditionEnums = RightConditionEnums.CONDITION_WAITEDWORK; } condition = new BPMCondition(BPMConditionKey.ACTIVITYHISTORY_DEALMETHOD, Operator.EQUALS, ActivityInstDealMethod.DEALMETHOD_NORMAL); if (processDefId != null) { BPMCondition ccondition = new BPMCondition(BPMConditionKey.ACTIVITYHISTORY_PROCESSDEF_ID, Operator.EQUALS, processDefId); condition.addCondition(ccondition, JoinOperator.JOIN_AND); } if (data.getEndTime() != null && data.getEndTime() != 0) { BPMCondition endcondition = new BPMCondition(BPMConditionKey.ACTIVITYHISTORY_ARRIVEDTIME, Operator.LESS_THAN, data.getEndTime()); condition.addCondition(endcondition, JoinOperator.JOIN_AND); } if (data.getTitle() != null && !data.getTitle().equals("")) { BPMCondition endcondition = new BPMCondition(BPMConditionKey.ACTIVITYHISTORY_PROCESSINST_ID, Operator.IN, "select PROCESSINST_ID from BPM_PROCESSINSTANCE where " BPMConditionKey.PROCESSINST_NAME " like '%" data.getTitle() "%'"); condition.addCondition(endcondition, JoinOperator.JOIN_AND); } condition.addOrderBy(new Order(BPMConditionKey.ACTIVITYHISTORY_ARRIVEDTIME, false)); try { ListResultModel<List<ActivityInstHistory>> activityInstList = getClient().getActivityInstHistoryList(condition, conditionEnums, null, null); result = PageUtil.changPageList(activityInstList, ActivityInstListView.class); } catch (BPMException e) { result = new ErrorListResultModel(); ((ErrorListResultModel<List<ActivityInstListView>>) result).setErrdes(e.getMessage()); ((ErrorListResultModel<List<ActivityInstListView>>) result).setErrcode(e.getErrorCode()); e.printStackTrace(); } return result; } /** * @return */ public ESDClient getEsdClient() throws JDSException { ESDClient client = ESDFacrory.getESDClient(); return client; } public String getProjectId() { return projectId; } public void setProjectId(String projectId) { this.projectId = projectId; } /** * @return */ public WorkflowClientService getClient() { JDSClientService service = JDSActionContext.getActionContext().Par(JDSClientService.class); if (service == null) { try { JDSActionContext.getActionContext().getSession().put("JDSUSERID", JDSServer.getInstance().getAdminUser().getId()); } catch (JDSException e) { e.printStackTrace(); } } WorkflowClientService client = ((WorkflowClientService) EsbUtil.parExpression("$BPMC")); return client; }}
发送控制建模OneCode源码示例
@Controller@RequestMapping("/bpm/custom/")@MethodChinaName(cname = "发送动作")@MenuBarMenu(menuType = CustomMenuType.bpm, dynLoad = true)@Aggregation(type=AggregationType.menu)public class PerformServiceImpl { @MethodChinaName(cname = "退回上一步") @RequestMapping(method = RequestMethod.POST, value = "RouteBack") @RouteCustomMenu(routeType = {RouteToType.RouteBack}) @APIEventAnnotation(callback = CustomCallBack.Reload, customRequestData = RequestPathEnum.ctx) public @ResponseBody ResultModel<Boolean> routeBack(String activityInstHistoryId, String activityInstId) { ResultModel resultModel = new ResultModel(); try { if (activityInstHistoryId == null) { ActivityInst inst = this.getClient().getActivityInst(activityInstId); activityInstHistoryId = inst.getRouteBackActivityHistoryInstList().get(0).getActivityHistoryId(); } this.getClient().routeBack(activityInstId, activityInstHistoryId, null); } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @MethodChinaName(cname = "收回") @RequestMapping(method = RequestMethod.POST, value = "TackBack") @RouteCustomMenu(routeType = {RouteToType.TackBack}) @APIEventAnnotation(callback = CustomCallBack.Reload, customRequestData = RequestPathEnum.ctx) public @ResponseBody ResultModel<Boolean> tackBack(String activityInstId) { ResultModel resultModel = new ResultModel(); try { this.getClient().takeBack(activityInstId, null); } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @RequestMapping(method = RequestMethod.POST, value = "SelectPerson") @NavTabsViewAnnotation(saveUrl = "bpm.custom.RouteTo") @ModuleAnnotation(caption = "选择人员", width = "400", dynLoad = true ) @RouteCustomMenu(routeType = {RouteToType.SelectPersons}) @DialogAnnotation @APIEventAnnotation(customRequestData = RequestPathEnum.ctx, isAllform = true) @ResponseBody public ListResultModel<List<NextActivityTree>> getSelectPerson(String activityInstId, String nextActivityDefId, RouteToType action) { ListResultModel<List<NextActivityTree>> performTree = new ListResultModel<>(); List<NextActivityTree> activityTrees = new ArrayList<>(); try { ActivityDef activityDef = this.getClient().getActivityDef(nextActivityDefId); for (PerformGroupEnums performGroup : PerformGroupEnums.values()) { NextActivityTree activityTree = new NextActivityTree(performGroup, activityInstId, activityDef, action); activityTrees.add(activityTree); } } catch (BPMException e) { e.printStackTrace(); } performTree.setData(activityTrees); return performTree; } @RequestMapping(method = RequestMethod.POST, value = "MultiSelect") @NavTabsViewAnnotation(saveUrl = "bpm.custom.MultiRouteto") @ModuleAnnotation(caption = "发送", width = "400", dynLoad = true) @DialogAnnotation @RouteCustomMenu(routeType = {RouteToType.MultiSelect}) @APIEventAnnotation(customRequestData = RequestPathEnum.ctx, isAllform = true) @ResponseBody public TreeListResultModel<List<NextActivityTree>> multiSelect(String activityInstId, RouteToType action) { TreeListResultModel<List<NextActivityTree>> performTree = new TreeListResultModel<>(); List<NextActivityTree> activityTrees = new ArrayList<>(); try { ActivityInst inst = this.getClient().getActivityInst(activityInstId); List<RouteDef> routeDefs = inst.getNextRoutes(); for (RouteDef routeDef : routeDefs) { ActivityDef activityDef = this.getClient().getActivityDef(routeDef.getToActivityDefId()); NextActivityTree nextActivityTree = new NextActivityTree(activityInstId, activityDef, action); activityTrees.add(nextActivityTree); } } catch (BPMException e) { e.printStackTrace(); } performTree.setData(activityTrees); return performTree; } @MethodChinaName(cname = "重新发送") @RequestMapping(method = RequestMethod.POST, value = "ReSend") @RouteCustomMenu(routeType = {RouteToType.ReSend}) @APIEventAnnotation(customRequestData = RequestPathEnum.ctx) public @ResponseBody ResultModel<Boolean> reSend(String activityInstHistoryId, String activityInstId) { ResultModel resultModel = new ResultModel(); try { List<Map<RightCtx, Object>> ctxs = new ArrayList<Map<RightCtx, Object>>(); List<String> activityDefIds = new ArrayList<String>(); ActivityInst activityInst = this.getClient().getActivityInst(activityInstId); if ((activityInstHistoryId == null || activityInstHistoryId.equals("")) && (this.getClient().getLastActivityInstHistoryListByActvityInst(activityInstId, null).size() > 0)) { ActivityInstHistory activityInstHistory = this.getClient().getLastActivityInstHistoryListByActvityInst(activityInstId, null).get(0); activityInstHistoryId = activityInstHistory.getActivityHistoryId(); } if (activityInstHistoryId == null && (this.getClient().getLastActivityInstHistoryListByActvityInst(activityInstId, null).size() > 0)) { ActivityInst hisactivityInst = this.getClient().copyActivityInstByHistory(activityInstHistoryId, null); RouteBean hisrouteBean = new RouteBean(); hisrouteBean.getPerforms().setPerforms(this.getClient().getConnectInfo().getUserID()); hisrouteBean.setNextActivityDefId(hisactivityInst.getActivityDefId()); hisrouteBean.setActivityInstId(hisactivityInst.getActivityInstId()); activityDefIds.add(hisactivityInst.getActivityDefId()); ctxs.add(this.fillCtx(hisrouteBean)); } else { RouteBean hisrouteBean = new RouteBean(); hisrouteBean.getPerforms().setPerforms(this.getClient().getConnectInfo().getUserID()); hisrouteBean.setNextActivityDefId(activityInst.getActivityDefId()); hisrouteBean.setAction(RouteToType.RouteTo); hisrouteBean.setActivityInstId(activityInst.getActivityInstId()); activityDefIds.add(activityInst.getActivityDefId()); ctxs.add(this.fillCtx(hisrouteBean)); } getClient().routeTo(activityInstId, activityDefIds, ctxs); } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @MethodChinaName(cname = "签收") @RouteCustomMenu(routeType = {RouteToType.SignReceive}) @RequestMapping(method = RequestMethod.POST, value = "SignReceive") @APIEventAnnotation(callback = CustomCallBack.Reload, customRequestData = RequestPathEnum.ctx) public @ResponseBody ResultModel<Boolean> signReceive(String activityInstId) { ResultModel resultModel = new ResultModel(); try { this.getClient().signReceive(activityInstId, null); } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @MethodChinaName(cname = "阅毕") @RequestMapping(method = RequestMethod.POST, value = "EndRead") @RouteCustomMenu(routeType = {RouteToType.EndRead}) @APIEventAnnotation(callback = CustomCallBack.Close, customRequestData = RequestPathEnum.ctx) public @ResponseBody ResultModel<Boolean> endRead(String activityInstId) { ResultModel resultModel = new ResultModel(); try { this.getClient().endRead(activityInstId, null); } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @MethodChinaName(cname = "自动推进") @RequestMapping(method = RequestMethod.POST, value = "AutoNext") @RouteCustomMenu(routeType = {RouteToType.AutoNext}) @APIEventAnnotation(callback = CustomCallBack.DynReload, customRequestData = RequestPathEnum.ctx) public @ResponseBody ResultModel<String> autoNext(String activityInstId, String processInstId, String nextActivityDefId) { ResultModel<String> resultModel = new ResultModel(); List<String> activityDefIds = new ArrayList<String>(); List<Map<RightCtx, Object>> ctxs = new ArrayList<Map<RightCtx, Object>>(); activityDefIds.add(nextActivityDefId); Map<RightCtx, Object> ctx = new HashMap<>(); List<String> performList = new ArrayList<>(); performList.add(this.getClient().getConnectInfo().getUserID()); ctx.put(RightCtx.PERFORMERS, performList); ctxs.add(ctx); try { this.getClient().routeTo(activityInstId, activityDefIds, ctxs); } catch (BPMException e) { e.printStackTrace(); } return resultModel; } @MethodChinaName(cname = "并行发送") @RequestMapping(method = RequestMethod.POST, value = "MultiRouteto") @RouteCustomMenu(routeType = {RouteToType.Multirouteto}) @APIEventAnnotation(bindMenu = {CustomMenuItem.save}, isAllform = true, callback = {CustomCallBack.CloseParent, CustomCallBack.Close}) public @ResponseBody ResultModel<Boolean> multiRouteto(@RequestBody RouteToBean routeToBean) { ResultModel resultModel = new ResultModel(); resultModel.setData(false); String activityInstId = routeToBean.getActivityInstId(); try { List<String> activityDefIds = new ArrayList<String>(); Map<String, PerformBean> routeBeans = routeToBean.getMultiSelect(); List<Map<RightCtx, Object>> ctxs = new ArrayList<Map<RightCtx, Object>>(); Set<String> keySet = routeBeans.keySet(); for (String nextActivityDefId : keySet) { RouteBean routeBean = routeBeans.get(nextActivityDefId).getPerformSelect(); routeBean.setNextActivityDefId(nextActivityDefId); routeBean.setActivityInstId(routeToBean.getActivityInstId()); routeBean.setAction(routeToBean.getAction()); activityDefIds.add(routeBean.getNextActivityDefId()); Map<RightCtx, Object> ctx = this.fillCtx(routeBean); ctxs.add(ctx); } getClient().routeTo(activityInstId, activityDefIds, ctxs); ActivityInst activityInst = this.getClient().getActivityInst(activityInstId); ActivityDefRight right = activityInst.getActivityDef().getRightAttribute();// // 当设定条件为单人办理且类型为自动签收时自动进入连续办理界面 if (right.getPerformSequence().equals(ActivityDefPerformSequence.AUTOSIGN) && activityInst.isCanSignReceive() && right.getPerformType().equals(ActivityDefPerformtype.SINGLE)) { resultModel.setData(true); } } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @MethodChinaName(cname = "发送") @RequestMapping(method = RequestMethod.POST, value = "RouteTo") @RouteCustomMenu(routeType = {RouteToType.RouteTo}) @APIEventAnnotation(bindMenu = {CustomMenuItem.save}, isAllform = true, callback = {CustomCallBack.CloseParent, CustomCallBack.Close}) public @ResponseBody ResultModel<Boolean> routeTo(@RequestBody WebPerformPerson webPerformPerson) { RouteBean routeBean = new RouteBean(); routeBean.setActivityInstId(webPerformPerson.getActivityInstId()); routeBean.setAction(webPerformPerson.getAction()); routeBean.setReaders(webPerformPerson.getSelectPerson().getReaders()); routeBean.setPerforms(webPerformPerson.getSelectPerson().getPerforms()); routeBean.setInsteadSigns(webPerformPerson.getSelectPerson().getInsteadSigns()); routeBean.setActivityInstHistoryId(webPerformPerson.getActivityInstHistoryId()); routeBean.setNextActivityDefId(webPerformPerson.getNextActivityDefId()); routeBean.setInsteadSigns(webPerformPerson.getSelectPerson().getInsteadSigns()); ResultModel resultModel = new ResultModel(); resultModel.setData(false); String activityInstId = routeBean.getActivityInstId(); try { List<String> activityDefIds = new ArrayList<String>(); List<Map<RightCtx, Object>> ctxs = new ArrayList<Map<RightCtx, Object>>(); activityDefIds.add(routeBean.getNextActivityDefId()); Map<RightCtx, Object> ctx = this.fillCtx(routeBean); ctxs.add(ctx); getClient().routeTo(activityInstId, activityDefIds, ctxs); ActivityInst activityInst = this.getClient().getActivityInst(activityInstId); ActivityDefRight right = activityInst.getActivityDef().getRightAttribute();// // 当设定条件为单人办理且类型为自动签收时自动进入连续办理界面 if (right.getPerformSequence().equals(ActivityDefPerformSequence.AUTOSIGN) && activityInst.isCanSignReceive() && right.getPerformType().equals(ActivityDefPerformtype.SINGLE)) { resultModel.setData(true); } } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @MethodChinaName(cname = "保存") @RequestMapping(method = RequestMethod.POST, value = "SaveOnly") @RouteCustomMenu(routeType = {RouteToType.SaveOnly}) @APIEventAnnotation(isAllform = true, customRequestData = {RequestPathEnum.form, RequestPathEnum.ctx}) public @ResponseBody ResultModel<Boolean> saveOnly(@RequestBody FormData data) { ResultModel resultModel = new ResultModel(); try { ActivityInst inst = this.getClient().getActivityInst(data.getActivityInstId()); DataMap map = inst.getFormValues(); Map<String, DataMap> dataMap = data.getTable(); map.putAll(dataMap); inst.updateFormValues(map); } catch (BPMException e) { e.printStackTrace(); } return resultModel; } @MethodChinaName(cname = "结束") @RequestMapping(method = RequestMethod.POST, value = "RouteToEnd") @RouteCustomMenu(routeType = {RouteToType.RouteToEnd}) @APIEventAnnotation(callback = CustomCallBack.Close, customRequestData = RequestPathEnum.ctx) public @ResponseBody ResultModel<Boolean> routeToEnd(String processInstId) { ResultModel resultModel = new ResultModel(); try { this.getClient().completeProcessInst(processInstId, null); } catch (BPMException e) { resultModel = new ErrorResultModel(); } return resultModel; } @MethodChinaName(cname = "查看历程") @RequestMapping(method = RequestMethod.POST, value = "ProcessView") @DynLoadAnnotation() @ModuleAnnotation(caption = "查看历程") @RouteCustomMenu(routeType = {RouteToType.View}) @ResponseBody public ResultModel<SVGProcessInstView> processView(String activityInstId, String nextActivityDefId) { ResultModel<SVGProcessInstView> resultModel = new ResultModel<SVGProcessInstView>(); return resultModel; } private Map<RightCtx, Object> fillCtx(RouteBean routeBean) throws BPMException { Map<RightCtx, Object> ctx = new HashMap<RightCtx, Object>(); ActivityDef activityDef = getClient().getActivityDef(routeBean.getNextActivityDefId()); // 办理类型 ActivityDefPerformtype performType = activityDef.getRightAttribute().getPerformType(); ActivityDefPerformSequence performSequence = activityDef.getRightAttribute().getPerformSequence(); if (performType.equals(ActivityDefPerformtype.NOSELECT) || performType.equals(ActivityDefPerformtype.NEEDNOTSELECT)) { List<String> readList = new ArrayList<String>(); List<String> performList = new ArrayList<String>(); List<Person> persons = activityDef.getRightAttribute().getPerFormPersons(); for (Person person : persons) { performList.add(person.getID()); } List<Person> readpersons = activityDef.getRightAttribute().getReaderPersons(); for (Person person : readpersons) { readList.add(person.getID()); } ctx.put(RightCtx.PERFORMERS, performList); ctx.put(RightCtx.READERS, readList); } else { String[] performArr = StringUtility.split(routeBean.getPerforms().getPerforms(), ";"); String[] readArr = StringUtility.split(routeBean.getReaders().getReaders(), ";"); List<String> performList = Arrays.asList(performArr); ctx.put(RightCtx.PERFORMERS, performList); ctx.put(RightCtx.READERS, Arrays.asList(readArr)); } return ctx; } /** * @return */ public WorkflowClientService getClient() { WorkflowClientService client = ((WorkflowClientService) EsbUtil.parExpression("$BPMC")); return client; }
(5)运行期动态渲染
传统低代码平台多数采用的是静态的渲染模型 ,一旦完成页面定义后,同一个页面只能有一种展现和渲染结果,这些在普通的互联网应用中一般能满足需求,但在企业应用中,特别是一些权限模型比较复杂系统中。将权限模型混合到页面中就会让组件的特性变得一团糟。而动态运算权限动态绘制则更是突破了普通低代码模型极限。
OneCode 则很好的弥补了这一遗憾,OnCode 在后端模型上提供了非常优良的动态API。
//系统单元测试中的 调用代码示例。
@RequestMapping(value = "/test/")public class TestService { @RequestMapping(value = "DynTestList") @DynLoadAnnotation(refClassName = "test.Search")// @DialogAnnotation @ModuleAnnotation(dynLoad = true)//表示页面加载为动态页面 @ResponseBody public ResultModel<EUModule> getTestList(EUModule module) { //注入动态参数 ResultModel resultModel = new ResultModel(); //添加一个JS方法到页面 module.getComponent().getCustomFunctions().put("test", "function(){}"); //查找所有输入域组件设为只读 List<InputComponent> components= module.getComponent().findComponents(ComponentType.Input,"%%"); for(InputComponent<InputProperties,InputEventEnum> component:components){ component.getProperties().setReadonly(true); } resultModel.setData(module); return resultModel; }}
五,内测系统示例揭秘
OneCode 这次首先发布了一个内侧版本,内侧版本中提供了相对比较详细的,OneCode 示例和源码。
从目录上看涵盖了,示例的组织机构代码、流程代码等等大家常用的建模源码。
内侧模块编译后的界面建模示例
DSM建模管理
由于篇幅关系,详细的内测版本源代码编译说明会在后续发布。
大家有兴趣可以回复获取邮箱,获取体验版版账号及编译说明。