Oinone协同开发使用手册

概述

Oinone平台为开发人员提供了本地环境 - 测试环境之间的协同开发模式,可以使得开发人员在本地环境中设计的模型、函数等元数据实时被测试环境使用并设计。开发人员开发完成对应页面和功能后,可以部署至测试环境直接进行测试。

本篇文章将详细介绍协同开发模式在实际开发中的应用及相关内容。

名词解释

  • 本地环境: 开发人员的本地启动环境
  • 测试环境: 在测试服务器上部署的业务测试环境,业务工程服务设计器服务共用中间件
  • 业务工程服务:在测试服务器上部署的业务工程
  • 设计器服务: 在测试服务器上部署的设计器镜像
  • 一套环境:以测试环境为例,业务工程服务设计器服务共同组成一套环境
  • 生产环境: 在生产服务器上部署的业务生产环境

环境准备

  1. 部署了一个可用的设计器服务,并能正常访问。(需参照下文启动设计器环境内容进行相应修改)
  2. 准备一个用于开发的java工程。
  3. 准备一个用于部署测试环境的服务器。

协同参数介绍

用于测试环境的参数

-PmetaProtected=${value}

启用元数据保护,只有配置相同启动参数的服务才允许对元数据进行更新。通常该命令用于设计器服务业务工程服务,并且两个环境需使用相同的元数据保护标记(value)进行启动。本地环境不使用该命令,以防止本地环境在协同开发时意外修改测试环境元数据,导致元数据混乱。

用法

java -jar boot.jar -PmetaProtected=pamirs

用于本地环境的配置

  • 使用命令配置ownSign(推荐)
java -jar boot.jar --pamirs.distribution.session.ownSign=demo
  • 使用yaml配置ownSign
pamirs:
  distribution:
    session:
      allMetaRefresh: false # 启用元数据全量刷新(备用配置,如遇元数据错误或混乱,启用该配置可进行恢复,使用一次后关闭即可)
      ownSign: demo # 协同开发元数据隔离标记,用于区分不同开发人员的本地环境,其他环境不允许使用

启动设计器环境

docker-run启动

-e PROGRAM_ARGS=-PmetaProtected=pamirs

docker-compose启动

services:
  backend:
    container_name: designer-backend
    image: harbor.oinone.top/oinone/designer-backend-v5.0
    restart: always
    environment:
      # 指定spring.profiles.active
      ARG_ENV: dev
      # 指定-Plifecycle
      ARG_LIFECYCLE: INSTALL
      # jvm参数
      JVM_OPTIONS: ""
      # 程序参数
      PROGRAM_ARGS: "-PmetaProtected=pamirs"

PS: java [JVM_OPTIONS?] -jar boot.jar [PROGRAM_ARGS?]

开发流程示例图

Oinone协同开发使用手册

具体使用步骤详见协同开发支持

Oinone社区 作者:张博昊原创文章,如若转载,请注明出处:https://doc.oinone.top/backend/14878.html

访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验

(0)
张博昊的头像张博昊数式管理员
上一篇 2024年7月23日 pm5:41
下一篇 2024年7月24日 pm1:49

相关推荐

  • 函数之触发与定时配置和示例

    异步任务总体介绍 函数的触发和定时在很多场景中会用到,也是一个oinone的基础能力。比如我们的流程产品中在定义流程触发时就会让用户选择模型触发还是时间触发,就是用到了函数的触发与定时能力。 触发任务TriggerTaskAction 触发任务的创建,使用sql-record模块监听mysql的binlog事件,通过rocketmq发送变更数据消息,收到MQ消息后,创建TriggerAutoTask。 触发任务的执行,使用TBSchedule拉取触发任务后,执行相应函数。 项目中引入依赖 1、项目的API工程引入依赖pamirs-core-trigger模块 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-api</artifactId> </dependency> 2、DemoModule在模块依赖定义中增加@Module(dependencies={TriggerModule.MODULE_MODULE}) @Component @Module( name = DemoModule.MODULE_NAME, displayName = "oinoneDemo工程", version = "1.0.0", dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, TriggerModule.MODULE_MODULE} ) @Module.module(DemoModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(PetShopProxy.MODEL_MODEL) public class DemoModule implements PamirsModule { ……其他代码 } 3、项目的boot工程引入依赖 <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-core</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-trigger-bridge-tbschedule</artifactId> </dependency> <dependency> <groupId>pro.shushi.pamirs.core</groupId> <artifactId>pamirs-sql-record-core</artifactId> </dependency> yml文件修改(applcation-xxx.yml) a. 修改pamris.event.enabled和pamris.event.schedule.enabled为trueb. pamirs_boot_modules增加启动模块:trigger、sql_record pamirs: record: sql: #改成自己路径 store: /opt/pamirs/logs … event: enabled: true schedule: enabled: true rocket-mq: namesrv-addr: 127.0.0.1:9876 boot: init: true sync: true modules: – base -…… – trigger – sql_record -…… 新建触发任务 新建PetTalentTrigger类,当PetTalent模型的数据记录被新建时触发系统做一些事情 package pro.shushi.pamirs.demo.core.trigger; import pro.shushi.pamirs.demo.api.model.PetTalent; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.trigger.annotation.Trigger; import pro.shushi.pamirs.trigger.enmu.TriggerConditionEnum; @Fun(PetTalent.MODEL_MODEL) @Slf4j public class PetTalentTrigger { @Function @Trigger(displayName = “PetTalent创建时触发”,name = “PetTalent#Trigger#onCreate”,condition = TriggerConditionEnum.ON_CREATE) public PetTalent onCreate(PetTalent data){ log.info(data.getName() + “,被创建”); //可以增加逻辑 return data; } } 定时任务 定时任务是一种非常常见的模式,这里就不介绍概念了,直接进入示例环节 新建PetTalentAutoTask实现ScheduleAction getInterfaceName()需要跟taskAction.setExecuteNamespace定义保持一致,都是函数的命名空间 taskAction.setExecuteFun("execute");跟执行函数名“execute”一致 TaskType需配置为CYCLE_SCHEDULE_NO_TRANSACTION_TASK,把定时任务的schedule线程分开,要不然有一个时间长的任务会导致普通异步或触发任务全部延时。 package pro.shushi.pamirs.demo.core.task; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.enmu.TimeUnitEnum; import pro.shushi.pamirs.demo.api.model.PetTalent; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.domain.fun.FunctionDefinition; import pro.shushi.pamirs.middleware.schedule.api.ScheduleAction; import pro.shushi.pamirs.middleware.schedule.common.Result; import pro.shushi.pamirs.middleware.schedule.domain.ScheduleItem; import pro.shushi.pamirs.middleware.schedule.eunmeration.TaskType; import pro.shushi.pamirs.trigger.enmu.TriggerTimeAnchorEnum; import pro.shushi.pamirs.trigger.model.ScheduleTaskAction; import pro.shushi.pamirs.trigger.service.ScheduleTaskActionService; @Slf4j @Component @Fun(PetTalent.MODEL_MODEL) public class PetTalentAutoTask implements…

    2024年5月25日
    1.4K00
  • 如何自定义覆盖内置模块的页面

    1.首先通过sql查询找到我们需要的页面,从其中的template字段复制出原视图的配置 通过模型编码model在base_view查找需要修改的视图 select * from base_view where model='workflow.WorkflowUserTask' and is_deleted = 0; 2.将base_view的template内容复制到java的core工程的resources目录下新建一个xml文件,修改里面的动作名称 <view widget="WorkFlowImplement"> <template slot="actions"> <action name="$$internal_GotoListTableRouter" priority="1" model="workflow.WorkflowUserTask" tag="contextFreeAction"/> <action name="approveStaging" widget="FlowTaskCommonAction" invisible="!(activeRecord.allowStaging)" priority="2" label="新暂存" model="workflow.WorkflowUserTask" displayName="新暂存"/> <action name="workflow_agree" invisible="!(activeRecord.allowAgree && activeRecord.status == &apos;ACTIVE&apos;)" priority="3" label="新同意" model="workflow.WorkflowUserTask" load="fetchDetailReadOnly" displayName="新同意" goBack="true" validateForm="true" loadRootData="true"/> <action name="workflow_rejust" invisible="!(activeRecord.allowReject && activeRecord.status == &apos;ACTIVE&apos;)" priority="4" label="新拒绝" model="workflow.WorkflowUserTask" displayName="新拒绝" goBack="true" loadRootData="true" tag="contextFreeAction"/> <action name="workflow_turnon" invisible="!(activeRecord.taskType == &apos;APPROVE&apos; && activeRecord.allowTransfer && activeRecord.status == &apos;ACTIVE&apos;)" priority="5" label="新转交" model="workflow.WorkflowUserTask" load="fetchDetailReadOnly" displayName="新转交" goBack="true" loadRootData="true" tag="contextFreeAction"/> <action name="workflow_addsign" invisible="!(activeRecord.taskType == &apos;APPROVE&apos; && activeRecord.allowAddSign && activeRecord.status == &apos;ACTIVE&apos;)" priority="6" label="新加签" model="workflow.WorkflowUserTask" load="fetchDetailReadOnly" displayName="新加签" goBack="true" loadRootData="true" tag="contextFreeAction"/> <action name="workflow_write_fallback" invisible="!(activeRecord.taskType == &apos;WRITE&apos; && activeRecord.allowFallback && activeRecord.status == &apos;ACTIVE&apos;)" priority="7" label="新回退" model="workflow.WorkflowUserTask" displayName="新回退" goBack="true" loadRootData="true" tag="contextFreeAction"/> <action name="workflow_sharing" priority="8" label="新分享" model="workflow.WorkflowUserTask" displayName="新分享" goBack="true" loadRootData="true" tag="contextFreeAction"/> </template> <template slot="fields"> <field name="remark" widget="TextArea" invisible="activeRecord.taskType != &apos;APPROVE&apos;" priority="8" model="workflow.WorkflowUserTask" data="remark" displayName="意见备注"/> </template> </view> 3.在生命周期的元数据编辑方法内覆盖视图 package pro.shushi.pamirs.demo.core.init; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand; import pro.shushi.pamirs.boot.common.extend.MetaDataEditor; import pro.shushi.pamirs.core.common.InitializationUtil; import pro.shushi.pamirs.demo.api.DemoModule; import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j; import pro.shushi.pamirs.meta.api.dto.meta.Meta; import pro.shushi.pamirs.meta.enmu.ViewTypeEnum; import pro.shushi.pamirs.workflow.app.api.model.WorkflowUserTask; import java.util.Map; @Slf4j @Component @Order(Ordered.LOWEST_PRECEDENCE) public class DemoModuleMetaInstall implements MetaDataEditor { @Override public void edit(AppLifecycleCommand command,…

    2024年7月2日
    1.4K00
  • 标品实施:从标品构建到定制(扩展)包的开发

    总体描述 Oinone有一个非常重要的特性:通过平台承载标准化产品(标品)。针对不同客户的个性化需求,不再直接修改标准产品代码,而是以扩展包的形式进行扩展和定制化,通过继承和重写标准产品的能力来满足客户需求。 本文讲解述怎么通过标品构建扩展工程的过程。 构建标品 按照Oinone的规范构建标品工程 构建扩展包 在定制模块中指定上游模块 上游依赖模块upstreams,模块定义如下: @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Module { // 显示名称 @AliasFor("displayName") String value() default ""; // 依赖模块名列表 String[] dependencies() default ModuleConstants.MODULE_BASE; // 上游模块名列表 String[] upstreams() default {}; …… 扩展模块示例 @Component @Module( name = SecondModule.MODULE_NAME, displayName = "DEMO扩展", version = "1.0.0", // 指定上游模块(标品模块,可以为多个) upstreams = DemoModule.MODULE_MODULE, priority = 1, dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE, AuthModule.MODULE_MODULE, BusinessModule.MODULE_MODULE, // 上游模块(标品模块,可以为多个) DemoModule.MODULE_MODULE, } ) @Module.module(SecondModule.MODULE_MODULE) @Module.Advanced(selfBuilt = true, application = true) @UxHomepage(@UxRoute(model = WorkRecord.MODEL_MODEL)) public class SecondModule implements PamirsModule { public static final String MODULE_MODULE = "demo_core_ext"; public static final String MODULE_NAME = "DemoCoreExt"; @Override public String[] packagePrefix() { return new String[]{ "pro.shushi.pamirs.second" }; } } application.yml配置文件 pamirs: boot: modules: ….. – demo_core // 加标准工程 – demo_core_ext maven配置 父工程依赖 <dependencyManagement> <dependencies> ….. <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-api</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-core</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> ….. </dependencies> </dependencyManagement> api子工程加入依赖 <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-api</artifactId> </dependency> boot子工程加入依赖 <dependency> <groupId>pro.shushi.pamirs.demo</groupId> <artifactId>pamirs-demo-core</artifactId> </dependency> 数据库设置 base数据库要跟标品工程一致 注意事项 标品工程的第三方依赖,在扩展工程都要有,否则启动会报错 扩展模块功能开发 菜单扩展 1、可以按需隐藏标品的菜单; 2、可以根据扩展包的实际情况增加菜单; 模型扩展 1、扩展包可继承标品已有模型; 新增字段、覆盖字段,不继承 2、扩展包可根据实际情况新增自有模型; 函数扩展 1、扩展包可根据实际情况【覆写】标品中的函数; 2、扩展包可根据实际情况【新增】自有函数; 3、扩展包可通过Hook机制实现业务的个性化; 4、扩展包可根据自身业务情况实现标品中的扩展点; 5、……

    2024年6月1日
    1.9K00
  • 自定义RSQL占位符(placeholder)及在权限中使用

    1 自定义RSQL占位符常用场景 统一的数据权限配置 查询表达式的上下文变量扩展 2 自定义RSQL的模板 /** * 演示Placeholder占位符基本定义 * * @author Adamancy Zhang at 13:53 on 2024-03-24 */ @Component public class DemoPlaceHolder extends AbstractPlaceHolderParser { private static final String PLACEHOLDER_KEY = "${thisPlaceholder}"; /** * 占位符 * * @return placeholder */ @Override public String namespace() { return PLACEHOLDER_KEY; } /** * 占位符替换值 * * @return the placeholder replace to the value */ @Override protected String value() { return PamirsSession.getUserId().toString(); } /** * 优先级 * * @return execution order of placeholders, ascending order. */ @Override public Integer priority() { return 0; } /** * 是否激活 * * @return the placeholder is activated */ @Override public Boolean active() { return true; } } 注意事项 在一些旧版本中,priority和active可能不起作用,为保证升级时不受影响,请保证该属性配置正确。 PLACEHOLDER_KEY变量表示自定义占位符使用的关键字,需按照所需业务场景的具体功能并根据上下文语义正确定义。 为保证占位符可以被正确替换并执行,所有占位符都不应该出现重复,尤其是不能与系统内置的重复。 3 占位符使用时的优先级问题 多个占位符在进行替换时,会根据优先级按升序顺序执行,如需要指定替换顺序,可使用Spring的Order注解对其进行排序。 import org.springframework.core.annotation.Order; @Order(0) 4 Oinone平台内置的占位符 占位符 数据类型 含义 备注 ${currentUser} String 当前用户ID 未登录时无法使用 ${currentRoles} Set<String> 当前用户的角色ID集合 未登录时无法使用 5 如何覆盖平台内置的占位符? 通过指定占位符的优先级,并定义相同的namespace可优先替换。 6 如何定义会话级别的上下文变量? 在上述模板中,我们使用的是Oinone平台内置的上下文变量进行演示,通常情况下,我们需要根据实际业务场景增加上下文变量,以此来实现所需功能。 下面,我们将根据当前用户获取当前员工ID定义该上下文变量进行演示。 /** * 员工Session * * @author Adamancy Zhang at 14:33 on 2024-03-24 */ @Component public class EmployeeSession implements HookBefore { private static final String SESSION_KEY = "CUSTOM_EMPLOYEE_ID"; @Autowired private DemoEmployeeService demoEmployeeService; public static String getEmployeeId() { return PamirsSession.getTransmittableExtend().get(SESSION_KEY);…

    2024年3月24日
    1.5K00
  • 如何通过自定义支持excel导出的动态表头

    介绍 本文需要阅读过前置文档如何自定义Excel导出功能,动态表头的功能在前置文档的基础上做的进一步扩展,本文未提到的部分都参考这个前置文档。 在日常的业务开发中,我们在导出的场景会遇到需要设置动态表头的场景,比如统计商品在最近1个月的销量,固定表头列为商品的名称等基础信息,动态表头列为最近一个月的日期,在导出的时候设置每个日期的销量,本文将通过此业务场景提供示例代码。 1.自定义导出任务模型 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(DemoItemDynamicExcelExportTask.MODEL_MODEL) @Model(displayName = "商品-Excel动态表头导出任务") public class DemoItemDynamicExcelExportTask extends ExcelExportTask { public static final String MODEL_MODEL = "demo.DemoItemDynamicExcelExportTask"; } 2.自定义导出任务处理数据的扩展点 package pro.shushi.pamirs.demo.core.excel.exportdemo.extPoint; import org.springframework.stereotype.Component; import pro.shushi.pamirs.core.common.FetchUtil; import pro.shushi.pamirs.core.common.cache.MemoryIterableSearchCache; import pro.shushi.pamirs.demo.api.model.DemoItem; import pro.shushi.pamirs.file.api.config.FileConstant; import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext; import pro.shushi.pamirs.file.api.enmu.ExcelTemplateTypeEnum; import pro.shushi.pamirs.file.api.entity.EasyExcelCellDefinition; import pro.shushi.pamirs.file.api.extpoint.impl.ExcelExportSameQueryPageTemplate; import pro.shushi.pamirs.file.api.model.ExcelExportTask; import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition; import pro.shushi.pamirs.file.api.util.ExcelFixedHeadHelper; import pro.shushi.pamirs.file.api.util.ExcelHelper; import pro.shushi.pamirs.file.api.util.ExcelTemplateInit; import pro.shushi.pamirs.framework.common.entry.TreeNode; import pro.shushi.pamirs.meta.annotation.ExtPoint; import pro.shushi.pamirs.meta.api.CommonApiFactory; import pro.shushi.pamirs.meta.api.core.orm.ReadApi; import pro.shushi.pamirs.meta.api.core.orm.systems.relation.RelationReadApi; import pro.shushi.pamirs.meta.api.dto.config.ModelConfig; import pro.shushi.pamirs.meta.api.dto.config.ModelFieldConfig; import pro.shushi.pamirs.meta.api.session.PamirsSession; import pro.shushi.pamirs.meta.enmu.TtypeEnum; import pro.shushi.pamirs.meta.util.FieldUtils; import java.util.*; @Component public class DemoItemDynamicExportExtPoint extends ExcelExportSameQueryPageTemplate<DemoItem> implements ExcelTemplateInit { public static final String TEMPLATE_NAME ="商品动态导出"; @Override public List<ExcelWorkbookDefinition> generator() { ExcelFixedHeadHelper excelFixedHeadHelper = ExcelHelper.fixedHeader(DemoItem.MODEL_MODEL,TEMPLATE_NAME) .createBlock(TEMPLATE_NAME, DemoItem.MODEL_MODEL) .setType(ExcelTemplateTypeEnum.EXPORT); return Collections.singletonList(excelFixedHeadHelper.build()); } public static void buildHeader(ExcelFixedHeadHelper excelFixedHeadHelper) { excelFixedHeadHelper.addColumn("name","名称") .addColumn("cateName","类目") .addColumn("searchFrom","搜索来源") .addColumn("description","描述") .addColumn("itemPrice","单价") .addColumn("inventoryQuantity","库存"); } @Override @ExtPoint.Implement(expression = "context.model == \"" + DemoItem.MODEL_MODEL+"\" && context.name == \"" +TEMPLATE_NAME+"\"" ) public List<Object> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) { List<Object> result = super.fetchExportData(exportTask,context); Object block = result.get(0); if (block instanceof ArrayList) { ((List<Object>) block).forEach(o -> { if (o instanceof DemoItem) { DemoItem item = (DemoItem) o; // TODO 设置动态表头部分字段的值 item.get_d().put("2024-09-10", "1111"); item.get_d().put("2024-09-11", "2222"); }…

    2024年9月11日
    3.3K00

Leave a Reply

登录后才能评论