标品实施:从标品构建到定制(扩展)包的开发

总体描述

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、……

Oinone社区 作者:望闲原创文章,如若转载,请注明出处:https://doc.oinone.top/kai-fa-shi-jian/13346.html

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

(0)
望闲的头像望闲数式管理员
上一篇 2024年6月1日 am11:40
下一篇 2024年6月1日 pm6:57

相关推荐

  • 使用Mapper方式进行联表查询

    有些业务场景需要查询两张表的数据,这时候就需要用到联表查询。下面将介绍两种方式进行联表查询。 场景:A模型页面,查询条件中包含B模型字段 模型A @Model.model(YesOne.MODEL_MODEL) @Model(displayName = "YesOne", summary = "YesOne") public class YesOne extends IdModel { public static final String MODEL_MODEL = "top.YesOne"; @Field.Integer @Field(displayName = "YesId") private Long yesId; @Field.String @Field(displayName = "名字") private String name; @Field.String @Field(displayName = "科目名字") private String professionalName; @Field(displayName = "关联YesTwo") @Field.many2one @Field.Relation(relationFields = {"yesId"},referenceFields = {"id"}) private YesTwo yesTwo; } 模型B @Model.model(YesTwo.MODEL_MODEL) @Model(displayName = "YesTwo", summary = "YesTwo") public class YesTwo extends IdModel { public static final String MODEL_MODEL = "top.YesTwo"; @Field.Integer @Field(displayName = "科目id") private Long professionalId; @Field.String @Field(displayName = "科目名字") private String professionalName; } 1. 使用in的方式查询 通过B模型的查询条件查询出符合条件的所有数据ID,再根据这个ID去A模型里面查询出所需的数据。 @Function.Advanced(displayName = "查询列表", type = FunctionTypeEnum.QUERY, category = FunctionCategoryEnum.QUERY_PAGE, managed = true) @Function(openLevel = {FunctionOpenEnum.LOCAL, FunctionOpenEnum.REMOTE, FunctionOpenEnum.API}) public Pagination<YesOne> queryPage(Pagination<YesOne> page, IWrapper<YesOne> queryWrapper) { String professionalName = (String) queryWrapper.getQueryData().get("professionalName"); if (StringUtils.isNotBlank(professionalName)) { List<Long> yesTwoId = new YesTwo().queryList(Pops.<YesTwo>lambdaQuery() .from(YesTwo.MODEL_MODEL) .eq(YesTwo::getProfessionalName, professionalName)) .stream().map(YesTwo::getId) .collect(Collectors.toList()); LambdaQueryWrapper<YesOne> wq = Pops.<YesOne>lambdaQuery().from(YesOne.MODEL_MODEL); if (CollectionUtils.isNotEmpty(yesTwoId)) { wq.in(YesOne::getYesId, yesTwoId); } return new YesOne().queryPage(page, wq); } return new YesOne().queryPage(page, queryWrapper); } 2. 使用mapper的方式查询 利用sql的方式去直接查询出结果。使用联表查询的方式查询 @Autowired private YesOneQueryMapper yesOneQueryMapper; @Function.Advanced(displayName = "查询列表", type = FunctionTypeEnum.QUERY, category = FunctionCategoryEnum.QUERY_PAGE, managed = true) @Function(openLevel = {FunctionOpenEnum.LOCAL,…

    2024年9月27日
    1.4K00
  • 如何使用GQL工具正确发起请求

    简介 本文将讲解一下如何正确发起GQL请求和GQL工具使用过程中的常见问题。 参数介绍 请求url和请求方法在浏览器的请求标头里面可以查到,保持一致就可以向服务正常发送请求,但还缺少请求体确定请求哪个接口。 每个请求都包括两部分内容:1. Query 2. Variables右键Query和Variables复制值直接拷贝到工具中就可以正常请求了。 注意:如果使用admin账号登录,请求的时候可以不携带Variables参数,因为admin没有权限控制,如果使用其他用户,就必须携带Variables参数,否则会被权限拦截。

    2024年10月25日
    1.5K00
  • Oinone协同开发使用手册

    概述 Oinone平台为开发人员提供了本地环境 – 测试环境之间的协同开发模式,可以使得开发人员在本地环境中设计的模型、函数等元数据实时被测试环境使用并设计。开发人员开发完成对应页面和功能后,可以部署至测试环境直接进行测试。 本篇文章将详细介绍协同开发模式在实际开发中的应用及相关内容。 名词解释 本地环境: 开发人员的本地启动环境 测试环境: 在测试服务器上部署的业务测试环境,业务工程服务和设计器服务共用中间件 业务工程服务:在测试服务器上部署的业务工程 设计器服务: 在测试服务器上部署的设计器镜像 一套环境:以测试环境为例,业务工程服务和设计器服务共同组成一套环境 生产环境: 在生产服务器上部署的业务生产环境 环境准备 部署了一个可用的设计器服务,并能正常访问。(需参照下文启动设计器环境内容进行相应修改) 准备一个用于开发的java工程。 准备一个用于部署测试环境的服务器。 协同参数介绍 用于测试环境的参数 -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?] 开发流程示例图 具体使用步骤详见协同开发支持

    2024年7月24日
    2.4K00
  • 函数之触发与定时配置和示例

    异步任务总体介绍 函数的触发和定时在很多场景中会用到,也是一个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.6K00
  • 字段类型之关系描述的特殊场景(常量关联)

    场景概述 【字段类型之关系与引用】一文中已经描述了各种关系字段的常规写法,还有一些特殊场景如:关系映射中存在常量,或者M2M中间表是大于两个字段构成。 场景描述 1、PetTalent模型增加talentType字段2、PetItem与PetTalent的多对多关系增加talentType(达人类型),3、PetItemRelPetTalent中间表维护petItemId、petTalentId以及talentType,PetDogItem和PetCatItem分别重写petTalents字段,关系中增加常量描述。示意图如下: 实际操作步骤 Step1 新增 TalentTypeEnum package pro.shushi.pamirs.demo.api.enumeration; import pro.shushi.pamirs.meta.annotation.Dict; import pro.shushi.pamirs.meta.common.enmu.BaseEnum; @Dict(dictionary = TalentTypeEnum.DICTIONARY,displayName = "达人类型") public class TalentTypeEnum extends BaseEnum<TalentTypeEnum,Integer> { public static final String DICTIONARY ="demo.TalentTypeEnum"; public final static TalentTypeEnum DOG =create("DOG",1,"狗达人","狗达人"); public final static TalentTypeEnum CAT =create("CAT",2,"猫达人","猫达人"); } Step2 PetTalent模型增加talentType字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.demo.api.enumeration.TalentTypeEnum; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; @Model.model(PetTalent.MODEL_MODEL) @Model(displayName = "宠物达人",summary="宠物达人",labelFields ={"name"}) public class PetTalent extends AbstractDemoIdModel{ public static final String MODEL_MODEL="demo.PetTalent"; @Field(displayName = "达人") private String name; @Field(displayName = "达人类型") private TalentTypeEnum talentType; } Step3 修改PetItem的petTalents字段,在关系描述中增加talentType(达人类型) @Field.many2many(relationFields = {"petItemId"},referenceFields = {"petTalentId","talentType"},through = PetItemRelPetTalent.MODEL_MODEL ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id","talentType"}) @Field(displayName = "推荐达人",summary = "推荐该商品的达人们") private List<PetTalent> petTalents; Step4 PetDogItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#1#"}, domain = " talentType == DOG") private List<PetTalent> petTalents; Step5 PetCatItem增加petTalents字段,重写父类PetItem的关系描述 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Field(displayName = "推荐达人") @Field.many2many( through = "PetItemRelPetTalent", relationFields = {"petItemId"}, referenceFields = {"petTalentId","talentType"} ) @Field.Relation(relationFields = {"id"}, referenceFields = {"id", "#2#"}, domain = " talentType == CAT") private List<PetTalent> petTalents; Step6 PetCatItem增加petTalents字段,many2one关系示例 talentType配置为常量,填入枚举的值 增加domain描述用户页面选择的时候自动过滤出特定类型的达人,RSQL用枚举的name @Model.model(PetPet.MODEL_MODEL) @Model(displayName…

    2024年5月25日
    2.0K00

Leave a Reply

登录后才能评论