跳至主要內容

枚举与数据字典

Mr.Hope大约 9 分钟

枚举与数据字典

枚举是大家在系统开发中经常用的一种类型,在oinone中也对枚举类型进行了支持,同时也做了相应的加强。希望通过本文能让大家对枚举的使用,有更全面的认知

枚举系统与数据字典

  1. 枚举系统(Enum System):
  • 枚举系统用于列举出一个有限序列集的所有成员。
  • 在编程中,枚举类型通常用于定义一组固定的常量集合,这些常量具有特定的含义或属性。
  • 枚举类型可以帮助开发者在编码过程中更清晰地表达代码的意图,提高代码的可读性和可维护性。
  • 例如,在Java中,可以使用枚举类型来定义一组状态、类型或选项,如颜色、星期几等。
  1. 数据字典(Data Dictionary):
  • 数据字典用于描述和管理系统中使用的各种数据和元数据。
  • 数据字典通常包含了系统中所有数据实体、属性、关系以及其它元数据的定义和描述。
  • 数据字典的目的是提供一个统一的、集中管理的地方,以便开发者和系统管理员查阅和维护系统中的所有数据和元数据信息。
  • 数据字典可以包含字段名称、数据类型、长度、约束条件、数据来源、数据含义等信息。
  • 数据字典在系统开发和维护过程中起到了重要的指导和管理作用,可以减少数据定义的混乱和冗余,提高系统的一致性和可维护性。

在系统开发中,枚举系统和数据字典通常会相互配合,枚举类型的定义可以作为数据字典中的一部分,用于描述系统中使用到的固定值,例如状态码、类型代码等。通过合理地设计和管理枚举系统和数据字典,可以提高系统的可靠性、可维护性和可扩展性。

枚举属性解释

@Dict(displayName = "",dictionary = "",name = "",summary = "",type = 1)

这段代码定义了一个名为 Dict 的注解,它具有以下属性:

  • dictionary(): 用于指定数据字典编码,标识该枚举与某个数据字典相关联。
  • displayName(): 用于指定枚举值在界面上的显示名称。
  • name(): 用于指定枚举值的技术名称,通常用于编程中的标识。
  • summary(): 用于提供枚举值的描述信息。
  • type(): 用于指定枚举值的类型,默认为1。

作用范围:

  • 注解标识在方法上。

该注解主要用于标识枚举类型,并提供了一些元数据信息,使得枚举值能够与数据字典关联,并在程序中进行合适的展示和描述。

协议约定

  1. 实现IEnum接口:
  • 枚举需要实现IEnum接口,这可能是一个自定义的接口,用于提供枚举值的一些基本属性和方法。
  1. 使用@Dict注解配置:
  • 使用@Dict注解来配置枚举,通过设置dictionary属性来指定数据字典的唯一编码。
  1. 前端展示和交互:
  • 前端使用枚举的displayName来展示枚举值,而枚举的name用于进行交互操作。
  1. 后端交互:
  • 后端使用枚举的value来进行交互,包括默认值的设置也使用枚举的value。
  1. 存储在数据字典表中:
  • 枚举会被存储在元数据的数据字典表中,以便在系统中进行统一管理和维护。
  1. 两类枚举:
  • 枚举分为异常类和业务类两种。
    • 异常类枚举用于定义程序中的错误提示信息,通常用于处理异常情况。
    • 业务类枚举用于定义业务中某个字段值的有穷有序集,用于约束和标识业务逻辑中的状态或类型。

通过遵循这些约定,可以在系统中更好地管理枚举类型,并确保枚举值的一致性和可维护性。这种约定也有助于前后端之间的统一和交互的顺利进行。

编程式用法

如果一个字段的类型被定义为枚举,则该字段就可以使用该枚举来进行可选项约束(options)。该字段的可选项为枚举所对应数据字典的子集。

可继承枚举

以下是继承BaseEnum的一些优点和功能:

  1. 实现Java不支持的枚举继承: Java本身不支持枚举的继承,但通过BaseEnum可以模拟实现这一功能,使得枚举可以像其他类一样进行继承。
  2. 动态创建枚举项: 基于BaseEnum的枚举可以在运行时动态地创建枚举项,这意味着在程序运行过程中可以根据需要动态地扩展枚举的内容,而不需要在编译时就确定所有的枚举项。
  3. 兼容无代码枚举: BaseEnum也可以与传统的无代码枚举兼容,即静态定义的枚举项,因为它们都可以通过相同的方式使用。
  4. 提供了继承的特性: 通过继承,枚举可以共享和继承父类的属性和方法,这使得枚举的设计更加灵活和模块化。

综上所述,继承BaseEnum为Java中的枚举提供了更多的功能和灵活性,使得枚举在实际应用中可以更好地满足各种需求,并且可以与现有的枚举模式兼容。

二进制枚举

通过 @Dict 注解设置数据字典的bit属性或者实现 BitEnum 接口来标识枚举值为2的次幂是一种方便的方法,可以在枚举中直观地表示枚举值的位操作特性。

enum不可继承枚举

package pro.shushi.pamirs.auth.api.enmu;

import pro.shushi.pamirs.meta.annotation.Dict;
import pro.shushi.pamirs.meta.common.enmu.IEnum;

@Dict(dictionary = "DemoTestEnum", displayName = "测试枚举")
public enum DemoTestEnum implements IEnum<String> {

    OPTION1("OPTION1", "测试1", "测试1"),
    OPTION2("OPTION2", "测试2", "测试2");

    private final String value;

    private final String displayName;

    private final String help;

    PermissionDataSourceEnum(String value, String displayName, String help) {
        this.value = value;
        this.displayName = displayName;
        this.help = help;
    }

    @Override
    public String value() {
        return value;
    }

    @Override
    public String displayName() {
        return displayName;
    }

    @Override
    public String help() {
        return help;
    }
}

BaseEnum可继承枚举

Step1 继承CatShapeEnum枚举

@Dict(dictionary = CatShapeEnum.DICTIONARY,displayName = "萌猫体型")
public class CatShapeEnum extends BaseEnum<CatShapeEnum,Integer> {
    public static final String DICTIONARY="demo.CatShapeEnum";
    
    public static final CatShapeEnum BIG=create("BIG",1,"大","大");
    public static final CatShapeEnum SMALL=create("SMALL",2,"小","小");

}
package pro.shushi.pamirs.demo.api.enumeration;

import pro.shushi.pamirs.meta.annotation.Dict;

@Dict(dictionary = CatShapeExEnum.DICTIONARY,displayName = "萌猫体型Ex")
public class CatShapeExEnum extends CatShapeEnum {
    public static final String DICTIONARY ="demo.CatShapeExEnum";
    
    public final static CatShapeExEnum MID =create("MID",3,"中","中");
}

Step2 修改PetCatType的shape字段类型为CatShapeExEnum

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

...//import

@Model.MultiTableInherited(type = PetCatType.KIND_CAT)
@Model.model(PetCatType.MODEL_MODEL)
@Model(displayName="萌猫品种",labelFields = {"name"})
public class PetCatType extends PetType {

    public static final String MODEL_MODEL="demo.PetCatType";
    public static final String KIND_CAT="CAT";

    @Field(displayName = "宠物分类",defaultValue = PetCatType.KIND_CAT,invisible = true)
    private String kind;

    @Field.Enum
    @Field(displayName = "宠物体型")
    private CatShapeExEnum shape;
}

Step3 可继承枚举的Switch API

继承BaseEnum可以实现Java不支持的可变枚举,可变枚举可以在运行时增加非Java代码定义的枚举项,同时可变枚举支持枚举继承。由于可变枚举不是Java规范中的枚举,所以无法使用switch...case...语句,但是K2提供稍作变化的switches(无需返回值)与switchGet(需要返回值)方式实现相同功能与逻辑。

BaseEnum.switches(比较变量, 比较方式/*系统默认提供两种方式:caseName()和caseValue()*/,
                  cases(枚举列表1).to(() -> {/*逻辑处理*/}),
                  cases(枚举列表2).to(() -> {/*逻辑处理*/}),
                  ...
                  cases(枚举列表N).to(() -> {/*逻辑处理*/}),
                  defaults(() -> {/*默认逻辑处理*/})
);
BaseEnum.<比较变量类型, 返回值类型>switchGet(比较变量, 
                                  比较方式/*系统默认提供两种方式:caseName()和caseValue()*/,
                cases(枚举列表1).to(() -> {/*return 逻辑处理的结果*/}),
                cases(枚举列表2).to(() -> {/*return 逻辑处理的结果*/}),
                ...
                cases(枚举列表N).to(() -> {/*return 逻辑处理的结果*/}),
                defaults(() -> {/*return 逻辑处理的结果*/})
);

caseName()使用枚举项的name与比较变量进行匹配比较;caseValue()使用枚举项的value值与比较变量进行匹配比较。 例如以下逻辑表示当ttype的值为O2O、O2M、M2O或M2M枚举值时返回true,否则返回false。

return BaseEnum.<String, Boolean>switchGet(ttype, caseValue(),
                cases(O2O, O2M, M2O, M2M).to(() -> true),
                defaults(() -> false)
);

二进制枚举

可以通过使用@Dict注解来设置数据字典的bit属性,或者通过实现BitEnum接口来标识该枚举值为2的次幂。二进制枚举的主要区别在于其值的序列化和反序列化方式与普通枚举不同。

Step1 新建店铺选项枚举、并添加为PetShop的一个字段

  1. PetShopOptionEnum继承BaseEnum<PetShopOptionEnum,Long>并实现BitEnum接口,增加三个枚举,值分别是2的0次幂,2的1次幂,2的2次幂。多选枚举3位枚举都选中,字段值为7
package pro.shushi.pamirs.demo.api.enumeration;

import pro.shushi.pamirs.meta.annotation.Dict;
import pro.shushi.pamirs.meta.common.enmu.BaseEnum;
import pro.shushi.pamirs.meta.common.enmu.BitEnum;

@Dict(dictionary = PetShopOptionEnum.DICTIONARY,displayName = "店铺选项枚举")
public class PetShopOptionEnum extends BaseEnum<PetShopOptionEnum,Long> implements BitEnum {
    public static final String DICTIONARY ="demo.PetShopOptionEnum";

    public static final PetShopOptionEnum ANCIENT = create("ANCIENT",1L<<0,"十年老店","十年老店");
    public static final PetShopOptionEnum SEVEN = create("SEVEN",1L<<1,"七天无理由退货","七天无理由退货");
    public static final PetShopOptionEnum CERTIFIED_PRODUCTS = create("CERTIFIED_PRODUCTS",1L<<2,"正品认证","正品认证");

}
  1. 修改PetShop,增加一个多选枚举字段options,枚举类型为PetShopOptionEnum
package pro.shushi.pamirs.demo.api.model;

import pro.shushi.pamirs.demo.api.enumeration.PetShopOptionEnum;
import pro.shushi.pamirs.meta.annotation.Field;
import pro.shushi.pamirs.meta.annotation.Model;

import java.sql.Time;
import java.util.List;

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

    @Field(displayName = "店铺标志")
    private List<PetShopOptionEnum> options;

}

异常枚举(举例)

作为Oinone管理异常的规范,一般枚举都是用 @Dict 声明为数据字典。然而,异常枚举会使用 @Error 进行注解,因为异常与业务枚举有很大区别。异常往往数量非常多,如果使用 @Dict 数据字典方式来管理,那么数据字典的量会非常庞大。

Step1 新建一个异常枚举类DemoExpEnumerate,实现ExpBaseEnum接口并加上@Errors(displayName = "demo模块错误枚举")注解,增加对应错误枚举

package pro.shushi.pamirs.demo.api.enumeration;
..//import

@Errors(displayName = "demo模块错误枚举")
public enum  DemoExpEnumerate implements ExpBaseEnum {

    SYSTEM_ERROR(ERROR_TYPE.SYSTEM_ERROR,90000000,"系统异常"),
    PET_SHOP_BATCH_UPDATE_SHOPLIST_IS_NULL(ERROR_TYPE.BIZ_ERROR,90000001,"店铺列表不能为空");

    private ERROR_TYPE type;
    private int code;
    private String msg;

    DemoExpEnumerate(ERROR_TYPE type, int code, String msg) {
        this.type= type;
        this.code=code;
        this.msg=msg;
    }
}

Step2 修改宠物商店批量更新数据状态逻辑

您可以在对 PetShopList 进行必选判断时,如果没有被选中,则抛出异常,并将异常枚举指定为 PET_SHOP_BATCH_UPDATE_SHOPLIST_IS_NULL。

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

...//import


@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();
        result.setPetShopList(petShopList);
        return result;
    }

    @Action(displayName = "确定",bindingType = ViewTypeEnum.FORM,contextType = ActionContextTypeEnum.SINGLE)
    public PetShopBatchUpdate conform(PetShopBatchUpdate data){
        if(data.getPetShopList() == null || data.getPetShopList().size()==0){
            throw  PamirsException.construct(DemoExpEnumerate.PET_SHOP_BATCH_UPDATE_SHOPLIST_IS_NULL).errThrow();
        }
        List<PetShopProxy> proxyList = data.getPetShopList();
        for(PetShopProxy petShopProxy:proxyList){
            petShopProxy.setDataStatus(data.getDataStatus());
        }
        new PetShopProxy().updateBatch(proxyList);
        return data;
    }

}