OneCode開源低代碼引擎技術(shù)揭秘(低代碼 開源)
《OneCode開源低代碼引擎白皮書》部分對(duì)于OneCode功能及組成做了詳細(xì)的描述。本文根據(jù)BeeCode團(tuán)隊(duì)開放的內(nèi)測(cè)版實(shí)測(cè),整理了技術(shù)揭秘系列,從產(chǎn)品的設(shè)計(jì)思想及技術(shù)原理方面來(lái)闡述以及平臺(tái)很對(duì)模型的賦能能力。本文主要針對(duì)的讀者是軟件專業(yè)領(lǐng)域的產(chǎn)品經(jīng)理、項(xiàng)目管理者、架構(gòu)師、程序員,如果您第一次閱讀本文需要優(yōu)先閱讀 《OneCode開源低代碼引擎白皮書》以方便對(duì)本文的理解。
一,OneCode 頂層設(shè)計(jì)
OneCode由三塊自成體系的可獨(dú)立部署運(yùn)行的部分組成。前端引擎負(fù)責(zé)界面建模并按低代碼協(xié)議協(xié)議生成標(biāo)準(zhǔn)JSON,中后臺(tái)OneCode通過(guò)讀取標(biāo)準(zhǔn)JSON協(xié)議,完成后端的視圖建模,合并DSM后端服務(wù)建模系統(tǒng),完成完整的后端服務(wù)建模應(yīng)用,通過(guò)代碼工程完成前后端一體的出碼應(yīng)用。JDSCloud是OneCode的協(xié)同支撐系統(tǒng),除了常規(guī)的資源代碼空間管理外,提供了獨(dú)立的沙箱運(yùn)行環(huán)境。為OneCode 提供工程化的仿真版本Ops等服務(wù)。
二,OneCode 建模過(guò)程
(1) 建模原理
用戶通過(guò),拖拽完成頁(yè)面建模序列化為按標(biāo)準(zhǔn)協(xié)議序列化JSON文件,后端OneCode服務(wù)支撐系統(tǒng)解析JSON文件并混合DSM建模信息以及后端服務(wù)邏輯后,通過(guò)混合編譯,通過(guò)代碼工廠指定出碼模板,完成前后端一體的編譯文件。
(1)拖拽建模
用戶由前端低代碼引擎提供拖拽支持,將用戶需求轉(zhuǎn)換為相應(yīng)的組件組合,完成建模后根據(jù)標(biāo)準(zhǔn)的低代碼組件序列化為JSON文件。
低代碼設(shè)計(jì)器組件庫(kù)
標(biāo)準(zhǔn)組件協(xié)議描述方式
(2)后端融合
后端融合系統(tǒng),通過(guò)讀取標(biāo)準(zhǔn)JSON組件,完成組件在后端的模型轉(zhuǎn)換。用戶可以通過(guò),DSM模型工具或者通過(guò)OneCode提供的建模通訊SDK,針對(duì)前端需要的服務(wù)調(diào)用邏輯以及業(yè)務(wù)數(shù)據(jù)邏輯,進(jìn)行建模干預(yù)。
(3)混合編譯出碼
完成后端建模的融合后,通過(guò)代碼工廠通過(guò)出碼配置輸出為OneCode源碼。
三,OneCode編譯原理
OneCode 本身基于JAVA語(yǔ)言體系,是在Java Spring 注解基礎(chǔ)上的一套擴(kuò)展子集,混合編譯引擎器通過(guò)擴(kuò)展注解構(gòu)建完整的Domain模型,通過(guò)讀取標(biāo)準(zhǔn)Spring 注解完成普通Web數(shù)據(jù)交付及調(diào)度過(guò)程,通過(guò)Domin域模型動(dòng)態(tài)渲染JS文件輸出為JSON交付給前端引擎構(gòu)建頁(yè)面。
編譯原理
轉(zhuǎn)換關(guān)系
表單實(shí)現(xiàn)
四,平臺(tái)賦能
(1)標(biāo)準(zhǔn)化能力
OneCode平臺(tái)在設(shè)計(jì)上將前端組件的設(shè)計(jì)上即采用了開放式設(shè)計(jì)模型及及存儲(chǔ)通訊標(biāo)準(zhǔn)。這將在很大程度上為各家低代碼平臺(tái)組件互聯(lián)互通提供便利,在設(shè)計(jì)上實(shí)現(xiàn)通用通行。避免形成應(yīng)用孤島。解決用戶被綁定在特定平臺(tái)的憂慮。
(2)混合編譯賦能
OneCode除了在前端實(shí)現(xiàn)了標(biāo)準(zhǔn)化組件定義外,還額外提供了后端建模的工具DSM,并通過(guò)領(lǐng)域模型將二者打通。這樣在前端組件建模時(shí)便可以直接調(diào)用后端服務(wù)模型完成數(shù)據(jù)部分API構(gòu)建。而DSM模型工具也可以在后端建模時(shí)直接讀取前端組件屬性,打通前端動(dòng)作與后端服務(wù)的通訊能力。
視圖設(shè)計(jì)器通過(guò),后端模型綁定插件快速選定后端Agg聚合服務(wù)模型接口,配置頁(yè)面快速綁定前后臺(tái)交互
后端DSM建模通過(guò)視圖模型擴(kuò)展直接修改操作,前端組件模型
二者模型的打通大幅降低了低代碼平臺(tái)的勞動(dòng)強(qiáng)度,同時(shí)通過(guò)針對(duì)模型的建模干預(yù)API,在模型建立重構(gòu)的方面可以從更深的層次上建立業(yè)務(wù)模型的構(gòu)建能力。
(3)混編檢測(cè)(預(yù)編譯)
傳統(tǒng)低代碼平臺(tái)基本上都是完全建立在JS的模型下,在初期建模時(shí)結(jié)構(gòu)還算清晰但經(jīng)過(guò)稍有點(diǎn)復(fù)雜的邏輯,構(gòu)建時(shí)代碼的冗余度以及結(jié)構(gòu)就會(huì)變得混亂,特別是頁(yè)跨頁(yè)面操作或者完成前后臺(tái)數(shù)據(jù)交互時(shí)。由于其腳本語(yǔ)言的特點(diǎn)無(wú)法完成實(shí)時(shí)校驗(yàn),只能運(yùn)行期測(cè)試才能發(fā)現(xiàn)問(wèn)題。采用低代碼構(gòu)建的頁(yè)面往往只是由于頁(yè)面中做了一些簡(jiǎn)單的組件增刪或者屬性樣式就該就會(huì)造成不可預(yù)期的結(jié)果,這大大降低了代碼的可維護(hù)度。OneCode所構(gòu)建的領(lǐng)域模型則很好的解決了這一問(wèn)題,在前后端任意模型發(fā)生變化時(shí)即可調(diào)用混合編譯,將頁(yè)面間的連接關(guān)系以及前后臺(tái)的數(shù)據(jù)關(guān)系進(jìn)行校驗(yàn)通知。在預(yù)編譯中提升整體的編譯能力。
(4)傳統(tǒng)低代碼服務(wù)賦能
在低代碼平臺(tái)有幾個(gè)領(lǐng)域是有很不錯(cuò)的實(shí)施效果的我們以最常見(jiàn)的表單流程模式來(lái)談一下OneCode賦能
流程 表單為核心應(yīng)用的BPM模式
用戶通過(guò)圖形界面繪制表單,然后通過(guò)自動(dòng)生成數(shù)據(jù)庫(kù)或者綁定特定數(shù)據(jù)庫(kù)字段完成數(shù)據(jù),再配合流程引擎完成數(shù)據(jù)流轉(zhuǎn)。傳統(tǒng)方案的優(yōu)勢(shì)在于其通過(guò)簡(jiǎn)化數(shù)據(jù)模型即后臺(tái)提升了快速定制的能力。但其劣勢(shì)也很明顯,但流程數(shù)據(jù)只能自成體系的完成運(yùn)轉(zhuǎn),一旦需要與外部系統(tǒng)交互只能通過(guò)代碼的二次開發(fā)來(lái)完成。而表單中如果需要嵌入外部數(shù)據(jù)源也必須要通過(guò)二次開發(fā)來(lái)處理。這就很大程度限制了其發(fā)展。
如果采用OneCode 可以在很大程度上可以緩和這個(gè)矛盾。采用OneCode模型后表單的后端模型能力將得到大幅提升,在流程定制過(guò)程中可以通過(guò)OneCode建模插件直接調(diào)用表單業(yè)務(wù)模型,動(dòng)態(tài)修改表單組件屬性,實(shí)現(xiàn)更高級(jí)別更細(xì)力度的展現(xiàn)控制。同時(shí)OneCode表單在建模時(shí)也可以同步調(diào)用流程模型,實(shí)時(shí)根據(jù)流轉(zhuǎn)邏輯完成相應(yīng)相應(yīng)。
流程定義干預(yù)示例
通過(guò)DSM模型后建立表單干預(yù)模型
表單干預(yù)事例
OneCode列表DSM建模模型
OneCode 流轉(zhuǎn)菜單建模
列表建模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 = "閱辦工作實(shí)例") @RequestMapping(method = RequestMethod.POST, value = "ReadActivityInstList") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmyuedu", caption = "閱辦工作實(shí)例") 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 = "閱閉工作實(shí)例") @RequestMapping(method = RequestMethod.POST, value = "EndReadActivityInstList") @CustomAnnotation(imageClass = "bpmfont bpmshouhui") @GridViewAnnotation @ModuleAnnotation(imageClass = "bpmfont bpmshouhui", caption = "閱閉工作實(shí)例") 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; }}
發(fā)送控制建模OneCode源碼示例
@Controller@RequestMapping("/bpm/custom/")@MethodChinaName(cname = "發(fā)送動(dòng)作")@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 = "發(fā)送", 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 = "重新發(fā)送") @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 = "自動(dòng)推進(jìn)") @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 = "并行發(fā)送") @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();// // 當(dāng)設(shè)定條件為單人辦理且類型為自動(dòng)簽收時(shí)自動(dòng)進(jìn)入連續(xù)辦理界面 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 = "發(fā)送") @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();// // 當(dāng)設(shè)定條件為單人辦理且類型為自動(dòng)簽收時(shí)自動(dòng)進(jìn)入連續(xù)辦理界面 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 = "結(jié)束") @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)運(yùn)行期動(dòng)態(tài)渲染
傳統(tǒng)低代碼平臺(tái)多數(shù)采用的是靜態(tài)的渲染模型 ,一旦完成頁(yè)面定義后,同一個(gè)頁(yè)面只能有一種展現(xiàn)和渲染結(jié)果,這些在普通的互聯(lián)網(wǎng)應(yīng)用中一般能滿足需求,但在企業(yè)應(yīng)用中,特別是一些權(quán)限模型比較復(fù)雜系統(tǒng)中。將權(quán)限模型混合到頁(yè)面中就會(huì)讓組件的特性變得一團(tuán)糟。而動(dòng)態(tài)運(yùn)算權(quán)限動(dòng)態(tài)繪制則更是突破了普通低代碼模型極限。
OneCode 則很好的彌補(bǔ)了這一遺憾,OnCode 在后端模型上提供了非常優(yōu)良的動(dòng)態(tài)API。
//系統(tǒng)單元測(cè)試中的 調(diào)用代碼示例。
@RequestMapping(value = "/test/")public class TestService { @RequestMapping(value = "DynTestList") @DynLoadAnnotation(refClassName = "test.Search")// @DialogAnnotation @ModuleAnnotation(dynLoad = true)//表示頁(yè)面加載為動(dòng)態(tài)頁(yè)面 @ResponseBody public ResultModel<EUModule> getTestList(EUModule module) { //注入動(dòng)態(tài)參數(shù) ResultModel resultModel = new ResultModel(); //添加一個(gè)JS方法到頁(yè)面 module.getComponent().getCustomFunctions().put("test", "function(){}"); //查找所有輸入域組件設(shè)為只讀 List<InputComponent> components= module.getComponent().findComponents(ComponentType.Input,"%%"); for(InputComponent<InputProperties,InputEventEnum> component:components){ component.getProperties().setReadonly(true); } resultModel.setData(module); return resultModel; }}
五,內(nèi)測(cè)系統(tǒng)示例揭秘
OneCode 這次首先發(fā)布了一個(gè)內(nèi)側(cè)版本,內(nèi)側(cè)版本中提供了相對(duì)比較詳細(xì)的,OneCode 示例和源碼。
從目錄上看涵蓋了,示例的組織機(jī)構(gòu)代碼、流程代碼等等大家常用的建模源碼。
內(nèi)側(cè)模塊編譯后的界面建模示例
DSM建模管理
由于篇幅關(guān)系,詳細(xì)的內(nèi)測(cè)版本源代碼編譯說(shuō)明會(huì)在后續(xù)發(fā)布。
大家有興趣可以回復(fù)獲取郵箱,獲取體驗(yàn)版版賬號(hào)及編譯說(shuō)明。