3.3.2 模型的类型

本文会介绍不同类型模型以及其简单的应用场景,方便大家理解不同类型模型的用途

模型分为元模型和业务模型。元数据是指描述应用程序运行所必需的数据、规则和逻辑的数据集;元模型是指用于描述内核元数据的一套模式集合;业务模型是指用于描述业务应用元数据的一套模式集合。

元模型分为模块域、模型域和函数域三个域。域的划分规则是根据元模型定义数据关联关系的离散性来判断,离散程度越小越聚集到一个域。在4.1.4【模块元数据详解】一文中介绍的ModuleDefinition就是元模型。而我们在开发中涉及的就是业务模型

一、模型类型

  • 抽象模型:往往是提供公共能力和字段的模型,它本身不会直接用于构建协议和基础设施(如表结构等)。

  • 传输模型:用于表现层和应用层之间的数据交互,本身不会存储,没有默认的数据管理器,只有数据构造器。

  • 存储模型:存储模型用于定义数据表结构和数据的增删改查(数据管理器)功能,是直接与连接器进行交互的数据容器。

  • 代理模型:用于代理存储模型的数据管理器能力的同时,扩展出非存储数据信息的交互功能的模型。

二、模型定义种类

模型定义就是模型描述,不同定义类型,代表计算描述模型的元数据的规则不同

  • 静态模型定义:模型元数据不持久化、不进行模型定义的计算(默认值、主键、继承、关联关系)

  • 静态计算模型定义:模型元数据不持久化但初始化时进行模型定义计算获得最终的模型定义

  • 动态模型定义:模型元数据持久化且初始化时进行模型定义计算获得最终的模型定义

静态模型定义需要使用@Model.Static进行注解;静态计算模型定义使用@Model.Static(compute=true)进行注解;动态模型定义不注解@Model.Static注解。

三、安装与更新

使用@Model.model来配置模型的不可变更编码。模型一旦安装,无法在对该模型编码值进行修改,之后的模型配置更新会依据该编码进行查找并更新;如果仍然修改该注解的配置值,则系统会将该模型识别为新模型,存储模型会创建新的数据库表,而原表将会rename为废弃表。

如果模型配置了@Base注解,表明在【oinone的设计器】中该模型配置不可变更;如果字段配置了@Base注解,表明在【oinone的设计器】中该字段配置不可变更。

四、基础配置

模型基类

所有的模型都需要继承以下模型中的一种,来表明模型的类型,同时继承以下模型的默认数据管理器(详见3.3.3模型的数据管理器一节)。

  • 继承BaseModel,构建存储模型,默认无id属性。

  • 继承BaseRelation,构建多对多关系模型,默认无id属性。

  • 继承TransientModel,构建临时模型(传输模型),临时模型没有数据管理器,也没有id属性。

  • 继承EnhanceModel,构建数据源为ElasticSearch的增强模型。

快捷继承

  • 继承IdModel,构建主键为id的模型。继承IdModel的模型会数据管理器会增加queryById方法(根据id查询单条记录)

  • 继承CodeModel,构建带有唯一编码code的主键为id的模型。可以使用@Model.Code注解配置编码生成规则。也可以直接覆盖CodeModel的generateCode方法或者自定义新增的前置扩展点自定义编码生成逻辑。继承CodeModel的模型会数据管理器会增加queryByCode方法(根据唯一编码查询单条记录)

  • 继承VersionModel,构建带有乐观锁,唯一编码code且主键为id的模型。

  • 继承IdRelation,构建主键为id的多对多关系模型。

模型继承关系图

3.3.2 模型的类型

图3-3-2-1 模型继承关系图

  • AbstractModel抽象基类是包含createDate创建时间、writeDate更新时间、createUid创建用户ID、writeUid更新用户ID、aggs聚合结果和activePks批量主键列表等基础字段的抽象模型。

  • TransientModel传输模型抽象基类是所有传输模型的基类,传输模型不存储,没有数据管理器。

  • TransientRelation传输关系模型是所有传输关系模型的基类,传输关系模型不存储,用于承载多对多关系,没有数据管理器。

  • BaseModel存储模型基类提供数据管理器功能,数据模型主键可以不是ID。

  • IdModel带id模型抽象基类,在BaseModel数据管理器基础之上提供根据ID查询、更新、删除数据的功能。

  • BaseRelation关系模型抽象基类用于承载多对多关系,是多对多关系的中间模型,数据模型主键可以不是ID。

  • IdRelation带id关系模型抽象基类,在BaseRelation数据管理器基础之上提供根据ID查询、更新、删除数据的功能。

  • CodeModel带code模型抽象基类,提供按配置生成业务唯一编码功能,根据code查询、更新、删除数据的功能。

  • EnhanceModel增强模型,提供全文检索能力。此模型会在4.1.25【框架之搜索引擎】一文中展开介绍。

五、抽象模型(举例)

抽象模型本身不会直接用于构建协议和基础设施(如表结构等),而是通过继承的机制供子模型复用其字段和函数。子模型可以是所有类型的模型。

比如demo模块要管理的一些公共模型字段,我们可以建一个AbstractDemoIdModel和AbstractDemoCodeModel,demo模块中的实体模型就可以继承它们。我们来为demo模块的模型统一增加一个数据状态这么一个字段,用做数据的生效与失效管理。

Step1 引入DataStatusEnum类

pamirs-demo-api的pom.xml包增加依赖,便于引入DataStatusEnum类,当然也可以自己建,这里只是oinone提供了统一的数据记录状态的枚举,以及相应的通用方法,这边就直接引入

<dependency>
    <groupId>pro.shushi.pamirs.core</groupId>
    <artifactId>pamirs-core-common</artifactId>
</dependency>

图3-3-2-2 引入通用类库

Step2 修改DemoModule

DataStatusEnum枚举类本身也会做为数据字典,以元数据的方式被管理起来,当一个模块依赖另一个模块的元数据相关对象,则需要改模块的模块依赖定义。为DemoModule增加CommonModule的依赖注解

package pro.shushi.pamirs.demo.api;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxHomepage;
import pro.shushi.pamirs.core.common.CommonModule;
import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Module;
import pro.shushi.pamirs.meta.base.PamirsModule;
import pro.shushi.pamirs.meta.common.constants.ModuleConstants;

@Component
@Module(
        name = DemoModule.MODULE_NAME,
        displayName = "oinoneDemo工程",
        version = "1.0.0",
        dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE}
)
@Module.module(DemoModule.MODULE_MODULE)
@Module.Advanced(selfBuilt = true, application = true)
@UxHomepage(@UxRoute(PetShop.MODEL_MODEL))
public class DemoModule implements PamirsModule {
    public static final String MODULE_MODULE = "demo_core";
    public static final String MODULE_NAME = "DemoCore";

    @Override
    public String[] packagePrefix() {
        return new String[]{ "pro.shushi.pamirs.demo"};
    }
}

图3-3-2-3 定义模块依赖

Step3 新建AbstractDemoCodeModel和AbstractDemoIdModel

并新增AbstractDemoIdModel和AbstractDemoCodeModel分别继承IdModel和CodeModel,实现IDataStatus接口不是必须的,刚好DataStatus有配套的通用逻辑,暂时也先加进去,具体使用会在本文的【代理模型】这段介绍

package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.core.common.behavior.IDataStatus;
import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.annotation.sys.Base;
import pro.shushi.pamirs.meta.base.common.CodeModel;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;

@Base
@Model.model(AbstractDemoCodeModel.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.ABSTRACT)
@Model(displayName = "AbstractDemoCodeModel")
public abstract class AbstractDemoCodeModel extends CodeModel implements IDataStatus {

    public static final String MODEL_MODEL="demo.AbstractDemoCodeModel";

    @Base
    @Field.Enum
    @Field(displayName = "数据状态",defaultValue = "DISABLED",required = true,summary = "作为基类给每一个继承模型增加一个数据状态字段")
    private DataStatusEnum dataStatus;
}

图3-3-2-4 新建AbstractDemoCodeModel

package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.core.common.behavior.IDataStatus;
import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.annotation.sys.Base;
import pro.shushi.pamirs.meta.base.IdModel;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;

@Base
@Model.model(AbstractDemoIdModel.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.ABSTRACT)
@Model(displayName = "AbstractDemoIdModel")
public abstract class AbstractDemoIdModel extends IdModel implements IDataStatus {

    public static final String MODEL_MODEL="demo.AbstractDemoIdModel";

    @Base
    @Field.Enum
    @Field(displayName = "数据状态",defaultValue = "DISABLED",required = true,summary = "作为基类给每一个继承模型增加一个数据状态字段")
    private DataStatusEnum dataStatus;
}

图3-3-2-5 新建AbstractDemoIdModel

Step4 修改PetShop的父类

修改PetShop父类从IdMode变更为AbstractDemoIdModel

package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import java.sql.Time;

@Model.model(PetShop.MODEL_MODEL)
@Model(displayName = "宠物店铺",summary="宠物店铺")
public class PetShop extends AbstractDemoIdModel {
    public static final String MODEL_MODEL="demo.PetShop";

    @Field(displayName = "店铺名称",required = true)
    private String shopName;

    @Field(displayName = "开店时间",required = true)
    private Time openTime;

    @Field(displayName = "闭店时间",required = true)
    private Time closeTime;
}

图3-3-2-6 修改PetShop的父类

重启看效果

宠物商店的列表页面和修改页面都增加数据状态字段,如下图3-3-2-7及3-3-2-8所示:

3.3.2 模型的类型

图3-3-2-7 宠物商店列表页增加数据状态字段

3.3.2 模型的类型

图3-3-2-8 宠物商店修改页增加数据状态字段

六、存储模型

存储模型用于定义数据表结构和数据的增删改查(数据管理器)功能,是直接与连接器进行交互的数据容器。

PetShop就是一个存储模型,这里就不过多举例子介绍了

七、代理模型(举例)

代理模型是用于代理存储模型的数据管理器能力,同时又可以扩展出非存储数据信息的交互功能的模型。

如果我们PetShop模型要展示创建人昵称?那么就可以建一个PetShopProxy类来完成,代理模型增加的字段都是非存储字段,只用于交互包括展示或提交

Step1 引入PamirsUser类

pamirs-demo-api的pom.xml包增加依赖,便于引入PamirsUser类。

<dependency>
    <groupId>pro.shushi.pamirs.core</groupId>
    <artifactId>pamirs-user-api</artifactId>
</dependency>

图3-3-2-9 引入基础类库user

Step2 新建PetShopProxy模型

新建一个PetShopProxy类继承PetShop,并声明类型为PROXY。同时增加一个creater字段

package pro.shushi.pamirs.demo.api.proxy;

import pro.shushi.pamirs.demo.api.model.PetShop;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ModelTypeEnum;
import pro.shushi.pamirs.user.api.model.PamirsUser;

@Model.model(PetShopProxy.MODEL_MODEL)
@Model.Advanced(type = ModelTypeEnum.PROXY)
@Model(displayName = "宠物店铺代理模型",summary="宠物店铺代理模型")
public class PetShopProxy extends PetShop {

    public static final String MODEL_MODEL="demo.PetShopProxy";

    @Field.many2one
    @Field(displayName = "创建者",required = true)
    @Field.Relation(relationFields = {"createUid"},referenceFields = {"id"})
    private PamirsUser creater;
}

图3-3-2-10 新建PetShopProxy模型

Step3 修改DemoModule

PamirsUser模型隶属于UserModule。为DemoModule增加UserModule的依赖注解,同时修改DemoModule的homepage注解,默认进入PetShopProxy的管理页面

package pro.shushi.pamirs.demo.api;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.boot.base.ux.annotation.action.UxRoute;
import pro.shushi.pamirs.boot.base.ux.annotation.navigator.UxHomepage;
import pro.shushi.pamirs.core.common.CommonModule;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.meta.annotation.Module;
import pro.shushi.pamirs.meta.base.PamirsModule;
import pro.shushi.pamirs.meta.common.constants.ModuleConstants;
import pro.shushi.pamirs.user.api.UserModule;

@Component
@Module(
        name = DemoModule.MODULE_NAME,
        displayName = "oinoneDemo工程",
        version = "1.0.0",
        dependencies = {ModuleConstants.MODULE_BASE, CommonModule.MODULE_MODULE, UserModule.MODULE_MODULE})
@Module.module(DemoModule.MODULE_MODULE)
@Module.Advanced(selfBuilt = true, application = true)
@UxHomepage(@UxRoute(PetShopProxy.MODEL_MODEL))
public class DemoModule implements PamirsModule {

    public static final String MODULE_MODULE = "demo_core";
    public static final String MODULE_NAME = "DemoCore";

    @Override
    public String[] packagePrefix() {
        return new String[]{ "pro.shushi.pamirs.demo"};
    }
} 

图3-3-2-11 定义模块依赖及修改首页入口

Step4 新增PetShopProxyAction类

为了展示效果覆盖PetShopProxy默认从PetShop继承的数据管理器方法,但对于Action和Function这些这里不展开介绍,具体会3.4【oinone以函数为内在】和3.5【oinone以交互为外在】两章中介绍。

  1. 新建一个PetShopProxyAction类,Model.model设置为PetShopProxy,也就是把所有aciton和function都挂在PetShopProxy这个模型载体上

  2. PetShopProxyAction继承DataStatusBehavior类配套dataStatus使用,定义一个【启用】serverAction,而启用逻辑则复用父类DataStatusBehavior的dataStatusEnable方法。

  3. 覆盖【queryPage】Function,该方法名为前后约定协议中列表查询的默认方法

package pro.shushi.pamirs.demo.core.action;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.core.common.behavior.impl.DataStatusBehavior;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.meta.annotation.Action;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.api.dto.condition.Pagination;
import pro.shushi.pamirs.meta.api.dto.wrapper.IWrapper;
import pro.shushi.pamirs.meta.constant.FunctionConstants;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;

@Model.model(PetShopProxy.MODEL_MODEL)
@Component
public class PetShopProxyAction extends DataStatusBehavior<PetShopProxy> {

    @Override
    protected PetShopProxy fetchData(PetShopProxy data) {
        return data.queryById();
    }
    @Action(displayName = "启用")
    public PetShopProxy dataStatusEnable(PetShopProxy data){
        data = super.dataStatusEnable(data);
        data.updateById();
        return data;
    }

    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    @Function.fun(FunctionConstants.queryPage)
    @Function(openLevel = {FunctionOpenEnum.API})
    public Pagination<PetShopProxy> queryPage(Pagination<PetShopProxy> page, IWrapper<PetShopProxy> queryWrapper){
        Pagination<PetShopProxy> result = new PetShopProxy().queryPage(page,queryWrapper);
        new PetShopProxy().listFieldQuery(result.getContent(),PetShopProxy::getCreater);
        return result;
    }

}

图3-3-2-12 新增PetShopProxyAction类

Step5 重启看效果

宠物商店的列表页面和修改页面都增加数据状态字段,如下图3-3-24所示:

3.3.2 模型的类型

图3-3-2-13 宠物商店页面效果

多次点击启用会报对应的错误。当然如果场景不需要,则没有必要复用DataStatusBehavior逻辑,也就不需要实现IDataStatus接口了,启动按钮只在禁用状态展示在后续的教程中会学到

3.3.2 模型的类型

图3-3-2-14 启用操作效果

八、传输模型(举例)

由于传输模型没有默认的数据管理器,只有数据构造器,所以在不自定义动作的情况下,传输模型可以打开详情页、新增表单和修改表单和列表页,但是所有的动作全部为窗口动作。传输模型本身不会存储,如果是关联关系字段关联传输模型,可以将传输模型序列化存储在模型的关联关系字段上。因为没有数据管理器,所以传输模型的列表页没有分页能力。

场景举例:如果我们想批量修改PetShop的数据状态,那么我们需要在列表页选中数据记录点击【批量修改】跳转到一个批量修改页面,选择要修改的数据状态,并点确认提交。那么传输模型就可以承载批量修改页面、数据以及操作的载体模型。

Step1 新建PetShopBatchUpdate模型

新建一个PetShopBatchUpdate类继承TransientModel,同时增加petShopList和dataStatus字段用于接收页面传值

package pro.shushi.pamirs.demo.api.tmodel;

import pro.shushi.pamirs.core.common.enmu.DataStatusEnum;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.base.TransientModel;

import java.util.List;

@Model.model(PetShopBatchUpdate.MODEL_MODEL)
@Model(displayName = "批量修改宠物商店数据状态",summary = "批量修改宠物商店数据状态")
public class PetShopBatchUpdate extends TransientModel {
    public static final String MODEL_MODEL="demo.PetShopBatchUpdate";

    @Field(displayName = "数据状态",required = true)
    private DataStatusEnum dataStatus;

    @Field(displayName = "宠物商店列表",required = true)
    @Field.many2many
    private List<PetShopProxy> petShopList;
}

图3-3-2-15 新建PetShopBatchUpdate模型

Step2 新增PetShopBatchUpdateAction类

传输模型没有默认的数据管理器,只有数据构造器(construct)。

  1. 新建一个PetShopBatchUpdateAction类,Model.model设置为PetShopBatchUpdate,也就是把所有Aciton和Function都挂在PetShopBatchUpdateAction这个模型载体上

  2. 覆盖数据构造器(construct),接收从宠物商店列表多选带过来的数据参数,非PetShopBatchUpdate本模型参数不能放第一个,用List petShopList来接收,进行数据组装逻辑处理,对应数据也是由PetShopBatchUpdate来承载返回给PetShopBatchUpdate的Form编辑页。

  3. 定义一个【确定】serverAction,绑定PetShopBatchUpdate的Form编辑页。点击确定则批量修改Form页面中的宠物商店列表为指定的数据状态

package pro.shushi.pamirs.demo.core.action;

import org.springframework.stereotype.Component;
import pro.shushi.pamirs.demo.api.proxy.PetShopProxy;
import pro.shushi.pamirs.demo.api.tmodel.PetShopBatchUpdate;
import pro.shushi.pamirs.meta.annotation.Action;
import pro.shushi.pamirs.meta.annotation.Function;
import pro.shushi.pamirs.meta.annotation.Model;
import pro.shushi.pamirs.meta.enmu.ActionContextTypeEnum;
import pro.shushi.pamirs.meta.enmu.FunctionOpenEnum;
import pro.shushi.pamirs.meta.enmu.FunctionTypeEnum;
import pro.shushi.pamirs.meta.enmu.ViewTypeEnum;

import java.util.List;

@Model.model(PetShopBatchUpdate.MODEL_MODEL)
@Component
public class PetShopBatchUpdateAction {

    @Function(openLevel = FunctionOpenEnum.API)
    @Function.Advanced(type= FunctionTypeEnum.QUERY)
    public PetShopBatchUpdate construct(PetShopBatchUpdate petShopBatchUpdate, List<PetShopProxy> petShopList){
        PetShopBatchUpdate result = new PetShopBatchUpdate();
        //前端传递只有id,补全下数据
        petShopList = FetchUtil.fetchList(petShopList);
        //补全创建者字段
        new PetShopProxy().listFieldQuery(petShopList,PetShopProxy::getCreater);
        result.setPetShopList(petShopList);
        return result;
    }

    @Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE)
    public PetShopBatchUpdate conform(PetShopBatchUpdate data){
        List<PetShopProxy> proxyList = data.getPetShopList();
        for(PetShopProxy petShopProxy:proxyList){
            petShopProxy.setDataStatus(data.getDataStatus());
        }
        new PetShopProxy().updateBatch(proxyList);
        return data;
    }
}

图3-3-2-16 新建PetShopBatchUpdateAction类

Step3 注解式初始化ViewAction窗口动作

在PetShopProxyAction增加类注解,并注释掉DemoModuleMetaDataEditor的viewActionInit方法体代码。用UxRouteButton注解来申明一个ViewAction。更多Ux系列注解详见3.5.4【Ux注解详解】一文

  1. @Model.model(PetShopProxy.MODEL_MODEL),代表UxRouteButton申明viewAction所在模型

  2. @UxRoute.model代表viewAction的resModel,路由的目标模型

@Model.model(PetShopProxy.MODEL_MODEL)
@UxRouteButton(action = @UxAction(name = "demo_petShop_batch_update", label = "批量更新数据状态",contextType = ActionContextTypeEnum.SINGLE_AND_BATCH), value = @UxRoute(model = PetShopBatchUpdate.MODEL_MODEL, viewType = ViewTypeEnum.FORM,openType = ActionTargetEnum.DIALOG,viewName= ViewConstants.Name.formView))
@Component
public class PetShopProxyAction extends DataStatusBehavior<PetShopProxy> {}

图3-3-2-17 在PetShopProxyAction增加类注解

Step4 重启看效果

3.3.2 模型的类型

图3-3-2-18 批量操作效果示意

Oinone社区 作者:史, 昂原创文章,如若转载,请注明出处:https://doc.oinone.top/oio4/9233.html

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

(2)
史, 昂的头像史, 昂数式管理员
上一篇 2024年5月23日 am9:49
下一篇 2024年5月23日 am9:51

相关推荐

  • 3.3.5 模型编码生成器

    在我们日常开发中经常要一些单据生成指定格式的编码,而且现在分布式环境下要考虑的事情会特别多。oinone提供了简易的编码生成能力 一、编码生成器 可以在模型或者字段上配置编码自动生成规则。在进行数据存储时,如果配置了编码自动生成规则的字段值为空,则系统将根据规则自动生成编码。编码自动生成功能是通过序列生成器来支持的。可以在序列生成器生成的序列编码基础上再进行组合配置的功能编码生成最终的编码。序列生成器可以配置初始序列,步长,日期格式,长度。 模型序列生成器(举例) 使用模型编码生成器,需要继承CodeModel或者有Code字段,那么使用Model.Code注解即可。 Step1 为PetShop增加一个@Model.Code注解,并增加一个店铺编码(Code)字段 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import java.sql.Time; @Model.model(PetShop.MODEL_MODEL) @Model(displayName = "宠物店铺",summary="宠物店铺",labelFields = {"shopName"}) @Model.Code(sequence = "DATE_ORDERLY_SEQ",prefix = "P",size=6,step=1,initial = 10000,format = "yyyyMMdd") public class PetShop extends AbstractDemoIdModel { public static final String MODEL_MODEL="demo.PetShop"; @Field(displayName = "店铺编码") private String code; @Field(displayName = "店铺名称",required = true) private String shopName; @Field(displayName = "开店时间",required = true) private Time openTime; @Field(displayName = "闭店时间",required = true) private Time closeTime; } 图3-3-5-1 为PetShop增加一个@Model.Code注解 Step2 重启查看效果 进入店铺新增页面新增一个oinone宠物店铺003 图3-3-5-2 示例操作效果图 查看店铺列表页面,新增的记录中店铺编码一列,已经按Model.Code注解要求地生成了 图3-3-5-3 示例操作效果图 Step3 小结 在我们日常开发中经常要一些单据生成指定格式的编码,而且现在分布式环境下要考虑的事情会特别多。oinone提供了简易的编码生成能力,大家可以根据编码注解说明,自行对PetShop模型进行不同配置,来学习编码生成器的知识 字段序列生成器 字段编码生成器,在对应的字段上增加,并使用Field.Sequence注解即可 Step1 为PetShop增加一个字段codeTwo并增加@Field.Sequence注解 package pro.shushi.pamirs.demo.api.model; import pro.shushi.pamirs.meta.annotation.Field; import pro.shushi.pamirs.meta.annotation.Model; import java.sql.Time; @Model.model(PetShop.MODEL_MODEL) @Model(displayName = "宠物店铺",summary="宠物店铺",labelFields = {"shopName"}) @Model.Code(sequence = "DATE_ORDERLY_SEQ",prefix = "P",size=6,step=1,initial = 10000,format = "yyyyMMdd") public class PetShop extends AbstractDemoIdModel { public static final String MODEL_MODEL="demo.PetShop"; @Field(displayName = "店铺编码") private String code; @Field(displayName = "店铺编码2") @Field.Sequence(sequence = "DATE_ORDERLY_SEQ",prefix = "C",size=6,step=1,initial = 10000,format = "yyyyMMdd") private String codeTwo; @Field(displayName = "店铺名称",required = true) private String shopName; @Field(displayName = "开店时间",required = true) private Time openTime; @Field(displayName = "闭店时间",required = true) private Time closeTime; } 图3-3-5-4 为PetShop增加一个字段codeTwo Step2 重启查看效果 进入店铺新增页面新增一个oinone宠物店铺004 图3-3-5-5 示例操作效果图 查看店铺列表页面,新增的记录中店铺编码2一列,已经按Field.Sequence注解要求地生成了 图3-3-5-6 示例操作效果图 二、编码注解说明,模型更多其他注解详见4.1.6【模型之元数据详解】…

    2024年5月23日
    1.6K00
  • 4.1.11 函数之异步执行

    异步任务是非常常见的一种开发模式,它在分布式的开发模式中有很多应用场景如: 高并发场景中,我们一般采用把长流程切短,用异步方式去掉可以异步的非关键功能,缩小主流程响应时间,提升用户体验 异构系统的集成调用,通过异步任务完成解耦与自动重试 分布式系统最终一致性的可选方案 今天我们了解oinone是如何结合Spring+TbSchedule来完成异步任务 一、TbSchedule介绍 它是一个支持分布式的调度框架,让批量任务或者不断变化的任务能够被动态的分配到多个主机的JVM中,在不同的线程组中并行执行,所有的任务能够被不重复,不遗漏的快速处理。基于ZooKeeper的纯Java实现,由Alibaba开源。在互联网和电商领域TBSchedule的使用非常广泛,目前被应用于阿里巴巴、淘宝、支付宝、京东、聚美、汽车之家、国美等很多互联网企业的流程调度系统。也是笔者早期在阿里参与设计的一款产品。 oinone的异步任务执行原理(如下图4-1-11-1所示),先做一个大致了解: 图4-1-11-1 Oinone的异步任务执行原理图 基础管理工具 下载tbSchedule的控制台jar包去除文件后缀.txt(详见本书籍【附件一】)pamirs-middleware-schedule-console-3.0.1.jar.txt(31.2 MB) 启动控制台 java -jar pamirs-middleware-schedule-console-3.0.1.jar 图4-1-11-2 控制台启动方式 访问地址 http://127.0.0.1:10014/schedule/index.jsp?manager=true 图4-1-11-3 访问地址 配置zk连接参数 图4-1-11-4 配置zk连接参数 oinone默认实现任务类型 图4-1-11-5 Oinone默认实现任务类型 baseScheduleNoTransactionTask baseScheduleTask remoteScheduleTask — 适用于pamirs-middleware-schedule独立部署场景 serialBaseScheduleNoTransactionTask serialBaseScheduleTask serialRemoteScheduleTask — 适用于pamirs-middleware-schedule独立部署场景 cycleScheduleNoTransactionTask delayMsgTransferScheduleTask deleteTransferScheduleTask 注: a. 默认情况下:所有任务的任务项都只配置了一个任务项0,只有一台机器能分配任务。 1. 如果要修改配置可以在启动项目中放置schedule.json,来修改配置 2. 人工进入控制修改任务对应任务项的配置 b. 如果想为某一个核心任务配置的独立调度器,不受其他任务执行影响。那么见独立调度的异步任务 任务表相关说明 图4-1-11-6 任务表相关说明 二、构建第一个异步任务(举例) Step1 新建PetShopService和PetShopServiceImpl 1 新建PetShopService定义updatePetShops方法 package pro.shushi.pamirs.demo.api.service; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import java.util.List; @Fun(PetShopService.FUN_NAMESPACE) public interface PetShopService { String FUN_NAMESPACE = "demo.PetShop.PetShopService"; @Function void updatePetShops(List<PetShop> petShops); } 图4-1-11-7 新建PetShopService定义updatePetShops方法 PetShopServiceImpl实现PetShopService接口并在updatePetShops增加@XAsync注解 displayName = "异步批量更新宠物商店",定义异步任务展示名称 limitRetryNumber = 3,定义任务失败重试次数,,默认:-1不断重试 nextRetryTimeValue = 60,定义任务失败重试的时间数,默认:3 nextRetryTimeUnit,定义任务失败重试的时间单位,默认:TimeUnitEnum.SECOND delayTime,定义任务延迟执行的时间数,默认:0 delayTimeUnit,定义任务延迟执行的时间单位,默认:TimeUnitEnum.SECOND package pro.shushi.pamirs.demo.core.service; import org.springframework.stereotype.Component; import pro.shushi.pamirs.demo.api.model.PetShop; import pro.shushi.pamirs.demo.api.service.PetShopService; import pro.shushi.pamirs.meta.annotation.Fun; import pro.shushi.pamirs.meta.annotation.Function; import pro.shushi.pamirs.trigger.annotation.XAsync; import java.util.List; @Fun(PetShopService.FUN_NAMESPACE) @Component public class PetShopServiceImpl implements PetShopService { @Override @Function @XAsync(displayName = "异步批量更新宠物商店",limitRetryNumber = 3,nextRetryTimeValue = 60) public void updatePetShops(List<PetShop> petShops) { new PetShop().updateBatch(petShops); } } 图4-1-11-8 实现PetShopService接口并在updatePetShops增加@XAsync注解 Step2 修改PetShopBatchUpdateAction的conform方法 引入PetShopService 修改conform方法 利用ArgUtils进行参数转化,ArgUtils会经常用到。 调用petShopService.updatePetShops方法 package pro.shushi.pamirs.demo.core.action; …… 引依赖类 @Model.model(PetShopBatchUpdate.MODEL_MODEL) @Component public class PetShopBatchUpdateAction { @Autowired private PetShopService petShopService; ……其他代码 @Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE) public PetShopBatchUpdate conform(PetShopBatchUpdate data){ if(data.getPetShopList() == null || data.getPetShopList().size()==0){ throw…

    2024年5月23日
    99900
  • 4.5.1 研发辅助之插件-结构性代码

    研发辅助意在 消灭研发过程中的重复性工作提升研发效率,如结构性代码 提供生产示例性代码,如果根据模型生成导入导出、view自定义配置等经常性开发 一、插件安装 根据自身Idea版本下载插件并安装: 版本 插件 2023.1 pamirs-source-maker-1.0.0-2023.1.zip(2.4 MB) 2021.1 pamirs-source-maker-1.0.0-2021.1.zip(2.4 MB) 2021.2 pamirs-source-maker-1.0.0-2021.2.zip(2.4 MB) 2021.3 pamirs-source-maker-1.0.0-2021.3.zip(2.4 MB) 2022.1 pamirs-source-maker-1.0.0-2022.1.zip(2.4 MB) pamirs-source-maker-1.0.0-223-EAP-SNAPSHOT(2.4 MB) 表4-5-1-1 插件列表 二、研发辅助之配置式结构性代码生成器 我们在开发过程中为了日后代码易于维护和修改,往往会做工程性的职责划分。 除去模型外会有 代理模型和代理模型Action来负责前端交互 以面向接口的形式来定义函数,就会有api和实现类之分 如果项目有多端,那么如代理模型和代理模型Action又要为每一个端构建一份 在大型项目的初始阶段,我们需要手工重复做很多事情,特别麻烦。现在用oinone的研发辅助插件的结构性代码生成器,就可以避免前面的重复工作 插件执行的配置文件 <?xml version="1.0" encoding="utf-8" ?> <oinone> <makers> <!– 根据模型生成代理类、代理类的Action、Service、ServiceImpl –> <maker> <!– 选择模型所在位置 –> <modelPath>/Users/oinone/Documents/oinone/demo/pamirs-second/pamirs-second-api/src/main/java/pro/shushi/pamirs/second/api/model</modelPath> <!– 代理模型、代理模型Action生成相关配置信息 –> <proxyModules> <module> <!– 代理模型和代理模型Action的生成位置信息 –> <generatePath>/Users/oinone/Documents/oinone/demo/pamirs-second/pamirs-second-api/src/main/java/pro/shushi/pamirs/second/api</generatePath> <!– 代理模型和代理模型Action的模块前缀 –> <modulePrefix>second</modulePrefix> <!– 代理模型和代理模型Action的模块名,代理模型和代理模型Action类名为moduleName+模型名+"Proxy"+"Action" –> <moduleName>second</moduleName> <!– 代理模型和代理模型Action的包名,实际包名为 packageName+".proxy"或packageName+".action"–> <packageName>pro.shushi.pamirs.second.api</packageName> </module> </proxyModules> <!– 根据模型生成api,包括service(写方法)和queryService(读方法) –> <apiModule> <!– service和queryService的生成位置信息 –> <generatePath>/Users/oinone/Documents/oinone/demo/pamirs-second/pamirs-second-api/src/main/java/pro/shushi/pamirs/second/api</generatePath> <!– service和queryService的模块前缀 –> <modulePrefix>second</modulePrefix> <!– service和queryService的模块名 –> <moduleName>second</moduleName> <!– service和queryService的包名,实际包名为 packageName+".service" –> <packageName>pro.shushi.pamirs.second.api</packageName> </apiModule> <!– 根据模型生成api实现类,包括serviceImpl(写方法)和queryServiceImpl(读方法) –> <coreModule> <!– serviceImpl和queryServiceImpl的生成位置信息 –> <generatePath>/Users/oinone/Documents/oinone/demo/pamirs-second/pamirs-second-core/src/main/java/pro/shushi/pamirs/second/core</generatePath> <!– serviceImpl和queryServiceImpl的模块前缀 –> <modulePrefix>second</modulePrefix> <!– serviceImpl和queryServiceImpl的模块名 –> <moduleName>second</moduleName> <!– serviceImpl和queryServiceImpl的包名,实际包名为 packageName+".service" –> <packageName>pro.shushi.pamirs.second.core</packageName> </coreModule> </maker> </makers> </oinone> 图4-5-1-1 插件执行的配置文件 三、研发辅助之多模型结构性代码生成器 是配置式结构性代码生成器的补充,应对开发后期维护中新增模型的场景。它的不同点在于只要选择模型文件就可以,不需要专门编写xml文件。生成的文件默认就在模型所在路径下 Step1 菜单栏上找到oinone,并点击子菜单【多模型结构性代码生成器】 图4-5-1-2 多模型结构性代码生成操作步骤一 Step2 设置必要的信息 模型前缀 模型的所属模块 代理模型的模块 这三个信息分别用于构建 代理模型的MODEL_MODEL = 模型前缀.代理模型的模块.代理模型类名 服务的FUN_NAMESPACE = 模型前缀.代理模型的模块.服务类名 图4-5-1-3 多模型结构性代码生成操作步骤二 Step3 选择为哪些模型生成对应的结构性代码 图4-5-1-4 多模型结构性代码生成操作步骤三 Step4 代码在模型所在目录 生成的文件默认就在模型所在路径下,您可以手动拖动到对应的包路径当中去 图4-5-1-5 多模型结构性代码生成操作步骤四

    2024年5月23日
    1.1K00
  • 3.5.7.8 自定义菜单栏

    在业务中,可能会遇到需要对菜单栏的交互或UI做全新设计的需求,这个时候可以自定义菜单栏组件支持。 首先继承平台的CustomMenuWidget 组件,将自己vue文件设置到component处 import { NavMenu, SPI, ViewWidget } from '@kunlun/dependencies'; import Component from './CustomMenu.vue'; @SPI.ClassFactory( ViewWidget.Token({ // 这里的widget跟平台的组件一样,这样才能覆盖平台的组件 widget: 'nav-menu' }) ) export class CustomMenuWidget extends NavMenu { public initialize(props) { super.initialize(props); this.setComponent(Component); return this; } } vue文件中继承平台的props,编写自定义页面代码 export const NavMenuProps = { /** * 当前模块 */ module: { type: Object as PropType<IModule | null> }, /** * 树结构的菜单 */ menus: { type: Array as PropType<IResolvedMenu[]>, default: () => [] }, /** * 菜单类型,现在支持垂直、水平、和内嵌模式三种 */ mode: { type: String as PropType<'vertical' | 'horizontal' | 'inline'>, default: 'inline' }, /** * 菜单栏是否折叠收起 */ collapsed: { type: Boolean, default: false }, /** * 当前展开的 SubMenu 菜单项 key 数组 */ openKeys: { type: Array as PropType<string[]>, default: () => [] }, /** * 当前选中的菜单项 key 数组 */ selectKeys: { type: Array as PropType<string[]>, default: () => [] }, /** * 菜单搜索下拉选中菜单项 */ selectMenuBySearch: { type: Function as PropType<(menuName: String) => void> }, /** * 选中菜单项 */ selectMenu: { type: Function as PropType<(menuName: String) => Promise<void>> }, /** * SubMenu 展开/关闭的回调 */ openChange: { type: Function as PropType<(openKeys: string[]) => void> }, /**…

    2024年5月23日
    1.1K00

Leave a Reply

登录后才能评论