芝法酱躺平攻略(6)——SpringBoot下的枚举的框架

8 篇文章 0 订阅
5 篇文章 1 订阅

一、背景与需求

1.1 哪些情况下使用枚举

在实际开发中,我们经常会遇到需要使用枚举的情况。比如性别,某种设备的类型,节点的状态等等。这些状态类型固定,数量也不怎么多,不需要放着数据库中,每次查询还做下没必要的联表操作。同时,即使放数据库中,也会面临如何表记这些状态的问题。到底用数字,还是用字符串。有没有可能操作不当而引起数据混乱等。

1.2 古早的解决方案

一种比较糟糕的做法是,在全局变量相关的文件中定义这些枚举,无论用数字还是字符串。

/*性别枚举*/
int GENDER_MALE = 1;//男性
int GENDER_FEMALE = 2;//女性
/*设备类型枚举*/
int BURRY_TYPE_BURIAL = 1;//直埋
int DEVICE_TYPE_OVERHEAD = 2//架空
int DEVICE_TYPE_EXPRESSWAY = 3//高速
int DEVICE_TYPE_PIPELINE = 4//管道
/*设备类型枚举*/
int VOLTAGE_V35 = 1; //35KV
int VOLTAGE_V110 = 2; //110KV
int VOLTAGE_V220 = 3; //220KV
int VOLTAGE_V500 = 4; //500KV
int VOLTAGE_VD800 = 5; //±800KV
int VOLTAGE_V1000 = 6; //1000KV
/*节点状态*/
int NODE_PROJECT = 1;//立项
int NODE_PROPREVIEW = 2;//项目预审
int NODE_BIND = 3;//标书
int NODE_CONTRACT = 4;//合同阶段
int NODE_MATERIAL = 5;//资料上传阶段

在写代码时,使用全局定义的枚举做逻辑判断

// 当节点确认时
if(NODE_PROPREVIEW  == node.state)
{
    // 流转到项目预审
}
else if(NODE_PROPREVIEW  == node.state)
{
	// 检测各专题分项是否合格
	// 进入选择供应商阶段
}

这种代码如果在团队成员稳定,并且注释写的比较详细规范时,其实也没有问题。但如果团队常常有人员流动,或者一份代码可能流转到另外的团队去维护。这种代码可能就会比较糟糕。别人可能不知道某各结构中的变量属于哪个枚举,该枚举的定义在哪里。更有甚者,可能一些不负责任的程序员直接在代码中写数字或字符串。使代码难以维护。

1.3 现代的枚举解决方案

在现代的编程语言中,往往会提供枚举的功能。在定义数据时,结构体的相关变量直接定义成枚举,这样就避免了1.2中谈及的尴尬问题。
如下面的代码:

@AllArgsConstructor
public enum ContractTypeEnum {
    DEFAULT(0, "default"),
    BINDING(1, "可研编制"),
    SUBJECT(2, "专题评估");

    @Getter
    @EnumValue
    int code;
    @Getter
    String desc;
}

ProjectEntity projectEntity = checkProject(contractEntity.getProjectId());
        switch (subjectType) {
            case BINDING:
                ProjectEvent projectSubmitEvent = new ProjectEvent(ProjectEventTypeEnum.PROJECT_CONTRACT_SUBMIT,
                        contractEntity.getId(), projectEntity, null, contractEntity);
                mApplicationContext.publishEvent(projectSubmitEvent);
                break;
            case SUBJECT:
                ProjectEvent SubjectSubmitEvent = new ProjectEvent(ProjectEventTypeEnum.SUBJECT_SIGN,
                        contractEntity.getId(), projectEntity, null, contractEntity);
                mApplicationContext.publishEvent(SubjectSubmitEvent);
                break;
        }
        return contractEntity;

1.4 前端下拉列表的挑战

在现代的开发中,往往有客户端,客户端需要知道某一类型的枚举都有哪些值,在前端如何表示。最常见的是注册账号时选择性别的下拉列表,设定设备时选择设备的类型等。
如果我们把枚举定义到了代码里,需要写一个通用的接口供前端。让前端通过枚举类型,查出对应枚举的各枚举值的信息以及一些其他相关信息。

1.5 有时类型需要动态添加枚举类型

有时候,枚举的类型数量也会发生变化,比如商品的类型。我们不希望每次商品类型增加时,所有相关微服务都要重新编译部署一遍,这大大增大了运营的成本和不确定性。
这种类型数据,我们认为不能算作枚举模块,因为接受该数据结构的字段,通常也是使用的诸如int、string的基础类型。故对于这种需求,我们还是使用传统的做法,在数据库中建立一个类型表。为了加速访问,我们可以使用redis做缓存。
为了减少前端的负担,我们可以把2种类型数据的查询使用统一的接口,让前端从这种细节中解脱出来,减少沟通的复杂度。

二、整体设计

2.1 需求应对

2.1.1 程序中的枚举

在程序中,我们可以把枚举都放在一个包下面,在程序启动时,通过扫描包的方式,把枚举相关信息加载道内存。
同时,我们还可以写一个自定义注解,加强枚举的信息。诸如下拉列表相关的配置。
在mybatis-plus中,天然支持实体字段使用枚举。

2.1.2 后台的类型枚举

对于后台需要动态添加的类型,我们可以创建2张数据表:
在这里插入图片描述
做一下这两张表的增删改查接口

2.1.3 程序枚举和后台枚举如何统一

可以在后台管理的服务中,顺便更新redis的缓存。在前端查询枚举时,先看程序枚举中有没有相关配置,没有的话就去redis里找找

2.2 程序架构

程序框架上,打算做3个模块。

2.2.1 framework-enums-core

放枚举相关的一些基础定义,枚举缓存的操作类等
在这里插入图片描述

pom文件引用如下:

<parent>
        <artifactId>framework-enums</artifactId>
        <groupId>indi.zhifa.recipe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>framework-enums-core</artifactId>
    <packaging>jar</packaging>

    <dependencies>

        <!--**********************web基础模块**************************-->
        <dependency>
            <groupId>indi.zhifa.recipe</groupId>
            <artifactId>framework-web-all</artifactId>
        </dependency>
        <!-- ******************lombok****************************-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

2.2.2 framework-enums-mgr

放后台类型的一些操作类
在这里插入图片描述
pom文件引用如下:

    <parent>
        <artifactId>framework-enums</artifactId>
        <groupId>indi.zhifa.recipe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>framework-enums-mgr</artifactId>
    <packaging>jar</packaging>


    <dependencies>
        <dependency>
            <groupId>indi.zhifa.recipe</groupId>
            <artifactId>framework-enums-core</artifactId>
        </dependency>
        <!-- ******************lombok****************************-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

2.2.3 framework-enums-cli

放枚举类的查询接口和方法
在这里插入图片描述
pom文件引用如下:

    <parent>
        <artifactId>framework-enums</artifactId>
        <groupId>indi.zhifa.recipe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>framework-enums-client</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>indi.zhifa.recipe</groupId>
            <artifactId>framework-enums-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

2.3 分项代码

2.3.1 framework-enums-core

2.3.1.1 EnumDesc

该文件是枚举的注解,加上该注解的枚举,才会被放进枚举缓存(前端才能查得到)。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnumDesc {
    /**
     * 中文名
     */
    String name() default "未知";
    /**
     * 描述
     */
    String desc() default "未知枚举";
    /**
     * 默认选择
     */
    int defaultIdx() default -1;
    /**
     * 默认选择
     */
    String defaultItem() default "全部";
}
2.3.1.2 IEnumVo

该文件定义了前端可访问的枚举接口,可以有多种形式的枚举。我们这里表现为程序中的枚举和动态类型的枚举。

public interface IEnumVo {
    /**
     * 获得枚举码(类名)
     */
    String getCode();

    /**
     * 获得枚举中文名
     */
    String getName();

    /**
     * 获得枚举描述
     */
    String getDescription();

    /**
     * 获得枚举子项
     */
    IEnumItemDto[] getItems();

    /**
     * 默认选择项,如果是-1,则是额外选择项
     */
    Integer getDefaultIdx();

    /**
     * 选择-1时的额外选择项,比如全部,默认,无
     */
    String getDefaultItem();
}
2.3.1.2 IEnumItemVo

该文件定义了枚举项的接口

public interface IEnumItemVo {

    /**
     * 获取枚举项Id,如1,101等
     */
    int getCode();

    /**
     * 获取枚举项的英文名,如MALE,FEMALE
     */
    String getName();

    /**
     * 获取枚举项中文名,如男,女
     */
    String getCname();

    /**
     * 获取枚举项的描述
     */
    String getDescription();

    /**
     * 获取枚举项的具体枚举常数,如EGender.MALE
     */
    Enum getEnumConstant();
}
2.3.1.3 EnumVo

程序枚举的Vo

@Data
@Schema(name = "枚举")
public class EnumVo implements IEnumVo {

    @Schema(name = "英文名")
    String code;

    @Schema(name = "中文名")
    String name;

    @Schema(name = "描述")
    String description;

    @Schema(name = "枚举项")
    EnumItemVo[] items;


    @Schema(name = "默认选择")
    Integer defaultIdx;

    @Schema(name = "默认选择描述")
    String defaultItem;

}
2.3.1.4 EnumVoDecorator

枚举VO的装饰器

public abstract class EnumVoDecorator implements IEnumVo {
    final IEnumVo enumDto;

    protected EnumVoDecorator(IEnumVo pEnumDto){
        enumDto = pEnumDto;
    }

    @Override
    public String getCode() {
        return enumDto.getCode();
    }
    @Override
    public String getName() {
        return enumDto.getName();
    }
    @Override
    public String getDescription() {
        return enumDto.getDescription();
    }
    @Override
    public IEnumItemVo[] getItems() {
        return enumDto.getItems();
    }

    public Integer getDefaultIdx(){
        return enumDto.getDefaultIdx();
    }

    public String getDefaultItem(){
        return enumDto.getDefaultItem();
    }
}
2.3.1.5 EnumNodeVo

枚举的节点,枚举中各枚举项被放入到map中

public class EnumNodeVo extends EnumVoDecorator {

    private Map<Integer, EnumItemVo> mapByCode;
    private Map<String, EnumItemVo> mapByName;
    private Map<String, EnumItemVo> mapByCName;

    public EnumNodeVo(EnumVo pEnumDto){
        super(pEnumDto);
        mapByName = new HashMap<>();
        mapByCName = new HashMap<>();
        mapByCode = new HashMap<>();
        init();
    }

    public void init(){
        EnumItemVo[] enumItemDtos = (EnumItemVo[])getItems();
        for(EnumItemVo enumItemDto : enumItemDtos){
            mapByCode.put(enumItemDto.getCode(),enumItemDto);
            mapByName.put(enumItemDto.getName(),enumItemDto);
            mapByCName.put(enumItemDto.getCname(),enumItemDto);
        }
    }

    public EnumItemVo getByCode(Integer pCode){
        EnumItemVo enumItemDto = mapByCode.get(pCode);
        if(null == enumItemDto){
            throw new ServiceException("没有找到Code为 "+pCode+" 的枚举项");
        }
        return enumItemDto;
    }
    public EnumItemVo getByName(String pName){
        EnumItemVo enumItemDto = mapByName.get(pName);
        if(null == enumItemDto){
            throw new ServiceException("没有找到名为 "+pName+" 的枚举项");
        }
        return enumItemDto;
    }

    public EnumItemVo getByCName(String pCName){
        EnumItemVo enumItemDto = mapByCName.get(pCName);
        if(null == enumItemDto){
            throw new ServiceException("没有找到名为 "+pCName+" 的枚举项");
        }
        return enumItemDto;
    }
}
2.3.1.6 SysTypeEntity

类型的数据库实体

@Data
@TableName("sys_type")
@Schema(title = "类型项", description = "类型项")
public class SysTypeEntity extends BaseEntity {

    @Schema(title = "类型码")
    protected String code;
    @Schema(title = "中文名")
    protected String name;
    @Schema(title = "描述")
    protected String description;
    @Schema(title = "默认选择",description = "-1表示默认选择非枚举项,可能是诸如全部,默认之类的选项")
    protected Integer defaultIdx;
    @Schema(title = "默认选择项",description = "默认")
    protected String defaultItem;
}
2.3.1.7 SysTypeItemEntity

类型项

@Data
@TableName("sys_type_item")
@Schema(title = "类型项", description = "类型项")
public class SysTypeItemEntity extends SysBaseEntity {
    @Schema(title = "类型", description = "类型")
    private Long typeId;

    @Schema(title = "类型码", description = "类型码")
    private String typeCode;

    @Schema(title = "类型项码", description = "类型项码")
    private int code;

    @Schema(title = "类型项英文名", description = "类型项英文名")
    private String name;

    @Schema(title = "类型项中文名", description = "类型项中文名")
    private String cname;

    @Schema(title = "类型项描述", description = "类型项描述")
    private String description;

    @Schema(title = "排序", description = "排序")
    private Integer sortOrder;
}
2.3.1.8 SysTypeItemVo

类型项VO

public class SysTypeItemVo extends SysTypeItemEntity implements IEnumItemVo {

    @Override
    public Enum getEnumConstant() {
        return null;
    }

}
2.3.1.9 SysTypeVo
@Schema(title = "类型VO")
public class SysTypeVo extends SysTypeEntity implements IEnumVo {
    @Setter
    @Schema(title = "类型项")
    List<SysTypeItemVo> items = new ArrayList<SysTypeItemVo>();

    @Override
    public IEnumItemVo[] getItems() {
        return items.toArray(new SysTypeItemVo[0]);
    }

}
2.3.1.10 ISysTypeMemo

类型缓存的操作接口

public interface ISysTypeMemo {
    void memo(SysTypeVo pSysTypeVo);
    void delete(SysTypeEntity pSysTypeVo);
    SysTypeVo infoFromMemo(long pId);
    SysTypeVo infoFromMemo(String pCode);
}
2.3.1.11 SysTypeMemoImpl

类型缓存的操作接口的实现

@Component
@RequiredArgsConstructor
public class SysTypeMemoImpl implements ISysTypeMemo {

    private final String TYPE_MGR_PREFIX = "TYPE_MGR:";
    private final String TYPE_MGR_CODE_PREFIX = "TYPE_MGR_CODE:";
    private final RedisUtil mRedisUtil;

    public void memo(SysTypeVo pSysTypeVo){
        mRedisUtil.set(TYPE_MGR_PREFIX+pSysTypeVo.getId(),pSysTypeVo);
        mRedisUtil.set(TYPE_MGR_CODE_PREFIX+pSysTypeVo.getCode(),pSysTypeVo.getId());
    }

    public void delete(SysTypeEntity pSysTypeVo){
        mRedisUtil.delete(TYPE_MGR_PREFIX+pSysTypeVo.getId());
        mRedisUtil.delete(TYPE_MGR_CODE_PREFIX+pSysTypeVo.getCode());
    }

    public SysTypeVo infoFromMemo(long pId){
        SysTypeVo sysTypeVo = mRedisUtil.get(TYPE_MGR_PREFIX+pId,SysTypeVo.class);
        return sysTypeVo;
    }

    public SysTypeVo infoFromMemo(String pCode){
        Long id = mRedisUtil.get(TYPE_MGR_CODE_PREFIX+pCode,Long.class);
        if(null == id){
            return null;
        }
        return infoFromMemo(id);
    }
}

2.3.2 framework-enums-mgr

2.3.2.1 dao层

由于代码都是mybatis-plus生成的,故略去一些,只展示dbService接口
ITypeDbService:

public interface ITypeDbService extends IZfDbService<SysTypeEntity> {
    boolean existByCode(String pCode);
    SysTypeEntity findByCode(String pCode);
}

ITypeItemDbService

public interface ITypeItemDbService extends IZfDbService<SysTypeItemEntity> {
    List<SysTypeItemEntity> listByTypeId(Long pTypeId);
    void removeByTypeId(Long pTypeId);
}
2.3.2.2 类型创建相关Dto

我更习惯在Dto层控制输入,所以创建、修改都会使用不同的dto
SysTypeCreateDto

@Data
@Schema(title = "类型创建DTO", description = "类型创建DTO")
public class SysTypeCreateDto {

    @Schema(title = "类型码")
    private String code;
    @Schema(title = "中文名")
    private String name;
    @Schema(title = "描述")
    private String description;
    @Schema(title = "默认选择",description = "-1表示默认选择非枚举项,可能是诸如全部,默认之类的选项")
    private Integer defaultIdx;
    @Schema(title = "默认选择项",description = "默认")
    private String defaultItem;
}

SysTypeEditDto

@Data
@Schema(title = "类型编辑DTO", description = "类型编辑DTO")
public class SysTypeEditDto {
    @Schema(title = "中文名")
    private String name;
    @Schema(title = "描述")
    private String description;
    @Schema(title = "默认选择",description = "-1表示默认选择非枚举项,可能是诸如全部,默认之类的选项")
    private Integer defaultIdx;
    @Schema(title = "默认选择项",description = "默认")
    private String defaultItem;
}

SysTypeItemCreateDto

@Data
@Schema(title = "类型项DTO", description = "类型项")
public class SysTypeItemCreateDto extends SysTypeItemEditDto{
    @Schema(title = "类型项码", description = "类型项码")
    protected Integer code;

    @Schema(title = "类型项英文名", description = "类型项英文名")
    protected String name;
}

SysTypeItemEditDto

@Data
@Schema(title = "类型修改DTO", description = "类型修改DTO")
public class SysTypeItemEditDto {
    @Schema(title = "类型项中文名", description = "类型项中文名")
    protected String cname;
    @Schema(title = "类型项描述", description = "类型项描述")
    protected String description;
}
SysTypeEditAllDto 
```java
@Data
@Schema(title = "类型项编辑Dto", description = "类型项编辑Dto")
public class SysTypeEditAllDto extends SysTypeEditDto{
    List<SysTypeItemCreateDto> items;
}
2.3.2.3 ITypeMgrService

类型增删改查的接口

public interface ITypeMgrService {

    void init();

    /**
     * 创建类型
     *
     * @param pSysTypeCfg  类型配置
     * @return
     */
    SysTypeVo create(SysTypeCreateDto pSysTypeCfg);

    SysTypeVo edit(Long pId, SysTypeEditDto pSysTypeEditCfg);

    /**
     *  修改类型信息
     *
     * @param pId   类型Id
     * @param pSysTypeEditAllDto 类型配置,包括子项
     * @return
     */
    SysTypeVo edit(Long pId, SysTypeEditAllDto pSysTypeEditAllDto);

    /**
     * 查看类型信息
     *
     * @param pId   类型Id
     * @return  类型VO
     */
    SysTypeVo info(Long pId);

    /**
     * 通过
     *
     * @param pCode
     * @return
     */
    SysTypeVo info(String pCode);

    /**
     * 新增类型项
     *
     * @param pTypeId  类型Id
     * @param pSysTypeItemCreateCfg 类型配置
     * @return
     */
    SysTypeItemEntity addItem(Long pTypeId, SysTypeItemCreateDto pSysTypeItemCreateCfg);

    /**
     * 编辑类型项
     *
     * @param pTypeItemId       类型Id
     * @param pSysTypeItemEditCfg      类型配置
     * @return 类型VO
     */
    SysTypeItemEntity editItem(Long pTypeItemId, SysTypeItemEditDto pSysTypeItemEditCfg);

    /**
     * 删除类型Id
     *
     * @param pTypeItemId   类型Id
     * @return  类型VO
     */
    boolean deleteTypeItem(Long pTypeItemId);

    /**
     * 删除类型
     *
     * @param pTypeId   类型Id
     * @return
     */
    boolean deleteType(Long pTypeId);
    
}
2.3.2.4 TypeMgrServiceImpl

实现类

@RequiredArgsConstructor
@ZfFacade(name = "类型管理")
public class TypeMgrServiceImpl implements ITypeMgrService {

    private final ITypeDbService mTypeDbService;
    private final ITypeItemDbService mTypeItemDbService;
    private final ISysTypeMemo mISysTypeMemo;

    public void init(){
        List<SysTypeEntity> sysTypeEntityList = mTypeDbService.list();
        List<SysTypeVo> sysTypeVoList = DtoEntityUtil.trans(sysTypeEntityList,SysTypeVo.class);
        for(SysTypeVo sysTypeVo : sysTypeVoList){
            List<SysTypeItemEntity> itemEntityList = mTypeItemDbService.listByTypeId(sysTypeVo.getId());
            List<SysTypeItemVo> sysTypeItemVoList =  DtoEntityUtil.trans(itemEntityList,SysTypeItemVo.class);
            sysTypeVo.setItems(sysTypeItemVoList);
            mISysTypeMemo.memo(sysTypeVo);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysTypeVo create(SysTypeCreateDto pSysTypeCfg) {
        if(mTypeDbService.existByCode(pSysTypeCfg.getCode())){
            throw new ServiceException("已经存在名为"+pSysTypeCfg.getCode()+"的类型");
        }
        SysTypeEntity typeEntity = DbDtoEntityUtil.createFromDto(pSysTypeCfg, SysTypeEntity.class);
        mTypeDbService.save(typeEntity);
        SysTypeVo sysTypeVo = DtoEntityUtil.trans(typeEntity,SysTypeVo.class);
        mISysTypeMemo.memo(sysTypeVo);
        return sysTypeVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysTypeVo edit(Long pId, SysTypeEditDto pSysTypeEditCfg) {
        SysTypeEntity orgSysTypeEntity = mTypeDbService.check(pId);
        SysTypeEntity newSysTypeEntity = DbDtoEntityUtil.editByDto(orgSysTypeEntity,pSysTypeEditCfg,SysTypeEntity.class);
        mTypeDbService.updateById(newSysTypeEntity);
        List<SysTypeItemEntity> sysTypeItemEntityList = mTypeItemDbService.listByTypeId(pId);
        SysTypeVo sysTypeVo = DtoEntityUtil.trans(newSysTypeEntity,SysTypeVo.class);
        List<SysTypeItemVo> sysTypeItemVoList =  DtoEntityUtil.trans(sysTypeItemEntityList,SysTypeItemVo.class);
        sysTypeVo.setItems(sysTypeItemVoList);
        mISysTypeMemo.memo(sysTypeVo);
        return sysTypeVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysTypeVo edit(Long pId, SysTypeEditAllDto pSysTypeEditAllDto) {
        SysTypeEntity orgSysTypeEntity = mTypeDbService.check(pId);
        SysTypeEntity newSysTypeEntity = DbDtoEntityUtil.editByDto(orgSysTypeEntity,pSysTypeEditAllDto,SysTypeEntity.class);
        mTypeDbService.updateById(newSysTypeEntity);
        mTypeItemDbService.removeByTypeId(pId);
        List<SysTypeItemEntity> typeItemEntityList = DbDtoEntityUtil.trans(pSysTypeEditAllDto.getItems(),SysTypeItemEntity.class);
        for(int i=0;i<typeItemEntityList.size();i++){
            SysTypeItemEntity sysTypeItemEntity = typeItemEntityList.get(i);
            sysTypeItemEntity.createInit();
            sysTypeItemEntity.setSortOrder(i+1);
            sysTypeItemEntity.setTypeId(pId);
            sysTypeItemEntity.setTypeCode(newSysTypeEntity.getCode());
        }
        mTypeItemDbService.saveBatch(typeItemEntityList);
        SysTypeVo sysTypeVo = DtoEntityUtil.trans(newSysTypeEntity,SysTypeVo.class);
        List<SysTypeItemVo> sysTypeItemVoList = DtoEntityUtil.trans(typeItemEntityList,SysTypeItemVo.class);
        sysTypeVo.setItems(sysTypeItemVoList);
        mISysTypeMemo.memo(sysTypeVo);
        return sysTypeVo;
    }

    protected SysTypeVo infoFromDb(Long pId){
        SysTypeEntity sysTypeEntity = mTypeDbService.check(pId);
        List<SysTypeItemEntity> sysTypeItemEntityList = mTypeItemDbService.listByTypeId(pId);
        SysTypeVo sysTypeVo = DtoEntityUtil.trans(sysTypeEntity,SysTypeVo.class);
        List<SysTypeItemVo> sysTypeItemVoList = DtoEntityUtil.trans(sysTypeItemEntityList,SysTypeItemVo.class);
        sysTypeVo.setItems(sysTypeItemVoList);
        return sysTypeVo;
    }

    @Override
    public SysTypeVo info(Long pId) {
        SysTypeVo sysTypeVo = mISysTypeMemo.infoFromMemo(pId);
        if(null == sysTypeVo){
            throw new ServiceException("缓存中没有找到id为"+pId+"的类型");
        }
        return sysTypeVo;
    }

    @Override
    public SysTypeVo info(String pCode) {
        SysTypeVo sysTypeVo = mISysTypeMemo.infoFromMemo(pCode);
        if(null == sysTypeVo){
            throw new ServiceException("缓存中没有找到code为"+pCode+"的类型");
        }
        return sysTypeVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysTypeItemEntity addItem(Long pTypeId, SysTypeItemCreateDto pSysTypeItemCreateCfg) {
        SysTypeEntity sysTypeEntity = mTypeDbService.check(pTypeId);
        SysTypeItemEntity sysTypeItemEntity = DbDtoEntityUtil.createFromDto(pSysTypeItemCreateCfg,SysTypeItemEntity.class);
        sysTypeItemEntity.setTypeId(sysTypeEntity.getId());
        sysTypeItemEntity.setTypeCode(sysTypeEntity.getCode());
        List<SysTypeItemEntity> sysTypeItemEntityList = mTypeItemDbService.listByTypeId(pTypeId);
        Set<Integer> codeSet = sysTypeItemEntityList.stream().map(SysTypeItemEntity::getCode).collect(Collectors.toSet());
        if(codeSet.contains(pSysTypeItemCreateCfg.getCode())){
            throw new ServiceException("类型"+sysTypeEntity.getCode()+"已存在编号为"+pSysTypeItemCreateCfg.getCode()+"的code的类型项");
        }
        Set<String> nameSet = sysTypeItemEntityList.stream().map(SysTypeItemEntity::getName).collect(Collectors.toSet());
        if(nameSet.contains(pSysTypeItemCreateCfg.getName())){
            throw new ServiceException("类型"+sysTypeEntity.getCode()+"已存在名为"+pSysTypeItemCreateCfg.getCode()+"的name的类型项");
        }

        sysTypeItemEntity.setSortOrder(sysTypeItemEntityList.size()+1);
        mTypeItemDbService.save(sysTypeItemEntity);
        SysTypeVo sysTypeVo = infoFromDb(pTypeId);
        mISysTypeMemo.memo(sysTypeVo);
        return sysTypeItemEntity;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysTypeItemEntity editItem(Long pTypeItemId, SysTypeItemEditDto pSysTypeItemEditCfg) {
        SysTypeItemEntity orgSysTypeItemEntity = mTypeItemDbService.check(pTypeItemId);
        SysTypeItemEntity newSysTypeItemEntity = DbDtoEntityUtil.editByDto(orgSysTypeItemEntity,pSysTypeItemEditCfg,SysTypeItemEntity.class);
        mTypeItemDbService.updateById(newSysTypeItemEntity);
        SysTypeVo sysTypeVo = infoFromDb(orgSysTypeItemEntity.getTypeId());
        mISysTypeMemo.memo(sysTypeVo);
        return newSysTypeItemEntity;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteTypeItem(Long pTypeItemId) {
        SysTypeItemEntity typeItemEntity = mTypeItemDbService.check(pTypeItemId);
        mTypeItemDbService.removeById(pTypeItemId);
        SysTypeVo sysTypeVo = infoFromDb(typeItemEntity.getTypeId());
        mISysTypeMemo.memo(sysTypeVo);
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteType(Long pTypeId) {
        SysTypeEntity sysTypeEntity = mTypeDbService.check(pTypeId);
        mTypeDbService.removeById(pTypeId);
        mISysTypeMemo.delete(sysTypeEntity);
        return true;
    }
}
2.3.2.5 SysTypeApi

类型的前端接口

@Api(tags = "1.后台类型管理")
@RequestMapping("/api/mgr/types")
@Slf4j
@ZfRestController
@RequiredArgsConstructor
public class SysTypeApi {

    final ITypeMgrService mTypeMgrService;

    @Operation(summary = "创建类型")
    @PostMapping("")
    SysTypeVo create( @Parameter(description = "类型配置") @RequestBody SysTypeCreateDto pSysTypeCfg){
        SysTypeVo sysTypeVo = mTypeMgrService.create(pSysTypeCfg);
        return sysTypeVo;
    }

    @Operation(summary = "修改类型")
    @PutMapping ("/{id}")
    SysTypeVo edit(
            @Parameter(description = "类型Id") @PathVariable(name = "id") Long pId,
            @Parameter(description = "类型配置") @RequestBody SysTypeEditDto pSysTypeEditCfg){
        SysTypeVo sysTypeVo = mTypeMgrService.edit(pId,pSysTypeEditCfg);
        return sysTypeVo;
    }

    @Operation(summary = "修改类型-全")
    @PutMapping ("/{id}/whole")
    SysTypeVo edit(
            @Parameter(description = "类型Id") @PathVariable(name = "id") Long pId,
            @Parameter(description = "类型配置") @RequestBody SysTypeEditAllDto pSysTypeEditCfg){
        SysTypeVo sysTypeVo = mTypeMgrService.edit(pId,pSysTypeEditCfg);
        return sysTypeVo;
    }



    @Operation(summary = "查询类型")
    @GetMapping ("/{id}")
    SysTypeVo info(
            @Parameter(description = "类型Id") @PathVariable(name = "id") Long pId){
        SysTypeVo sysTypeVo = mTypeMgrService.info(pId);
        return sysTypeVo;
    }

    @Operation(summary = "查询类型-code")
    @GetMapping ("/code/{code}")
    SysTypeVo info(
            @Parameter(description = "类型Id") @PathVariable(name = "code") String pCode){
        SysTypeVo sysTypeVo = mTypeMgrService.info(pCode);
        return sysTypeVo;
    }


    @Operation(summary = "添加类型项")
    @PostMapping ("/{typeId}/items/create")
    SysTypeItemEntity addItem(
            @Parameter(description = "类型Id") @PathVariable(name = "typeId") Long pTypeId,
            @Parameter(description = "类型项配置") @RequestBody SysTypeItemCreateDto pSysTypeItemCreateCfg){
        SysTypeItemEntity sysTypeItemEntity = mTypeMgrService.addItem(pTypeId,pSysTypeItemCreateCfg);
        return sysTypeItemEntity;
    }

    @Operation(summary = "修改类型项")
    @PostMapping ("/{typeId}/items/{id}")
    SysTypeItemEntity editItem(
            @Parameter(description = "类型Id") @PathVariable(name = "id") Long pId,
            @Parameter(description = "类型项配置") @RequestBody SysTypeItemEditDto pSysTypeItemEditCfg){
        SysTypeItemEntity sysTypeItemEntity = mTypeMgrService.editItem(pId,pSysTypeItemEditCfg);
        return sysTypeItemEntity;
    }

    @DeleteMapping ("/{id}")
    @Operation(summary = "删除类型")
    Boolean deleteTypeItem(
            @Parameter(description = "类型Id") @PathVariable(name = "id") Long pId){
        return mTypeMgrService.deleteType(pId);
    }

    @DeleteMapping ("/items/{id}")
    @Operation(summary = "删除类型项")
    boolean deleteType(
            @Parameter(description = "类型Id") @PathVariable(name = "id") Long pId){
        return mTypeMgrService.deleteTypeItem(pId);
    }
}

2.3.3 framework-enums-client

2.3.3.1 EnumMemoConfig

枚举包的路径配置

@Data
@Configuration
@ConfigurationProperties(prefix = "enum-memo")
public class EnumMemoConfig {
    String[] enumPackages;
}
2.3.3.2 IEnumMemoService

枚举缓存的接口

public interface IEnumMemoService {

    /**
     * 初始化Enum
     *
     */
    void initEnum(String[] pEnumBasePackages) throws InvocationTargetException, IllegalAccessException;


    /**
     * 筛选Enum
     *
     * @param pName enum名
     * @return Map<String, List < EnumDto>> 所有的Enum
     */
    IEnumVo findByName(String pName) throws ServiceException;

    /**
     * 使用枚举查找枚举节点
     *
     * @param pCls 类
     * @return EnumNodeDto 枚举节点
     */
    EnumNodeVo findByClass(Class pCls);

    /**
     * 获取所有的枚举
     *
     * @return List<EnumDto> 枚举列表
     */
    EnumVo[] list();

    /**
     * 添加枚举
     *
     * @param pEnumDto 枚举
     */
    void addEnumDto(EnumVo pEnumDto);
}
2.3.3.3 EnumMemoServiceImpl

枚举缓存的接口的实现

@Service
public class EnumMemoServiceImpl implements IEnumMemoService {

    public Map<String, EnumVo> enumsByName;
    public Map<Class, EnumNodeVo> enumMemoByClas;
    private final ISysTypeMemo mSysTypeMemo;

    public EnumMemoServiceImpl(){
        enumsByName = new HashMap<String, EnumVo>();
        enumMemoByClas = new HashMap<Class, EnumNodeVo>();
        mSysTypeMemo = SpringUtil.getBean(SysTypeMemoImpl.class);
    }

    @Override
    public void initEnum(String[] pEnumBasePackages) throws InvocationTargetException, IllegalAccessException {
        List<Class<?>> classes = new ArrayList<>();
        for(String path : pEnumBasePackages){
            Set<Class<?>> cls = ClassUtil.scanPackage(path);
            classes.addAll(cls);
        }
        for(Class cls : classes){
            if(!ClassUtil.isEnum(cls)){
                continue;
            }
            EnumDesc enumDesc = AnnotationUtils.findAnnotation(cls,EnumDesc.class);
            if(null == enumDesc){
                continue;
            }
            EnumVo enumDto = new EnumVo();
            String enumCode = cls.getSimpleName();
            enumDto.setCode(enumCode);
            enumDto.setName(enumDesc.name());
            enumDto.setDescription(enumDesc.desc());
            enumDto.setDefaultIdx(enumDesc.defaultIdx());
            enumDto.setDefaultItem(enumDesc.defaultItem());
            Object[] constants = cls.getEnumConstants();
            Method codeMethod = ClassUtil.getDeclaredMethod(cls, "getCode");
            Method nameMethod = ClassUtil.getDeclaredMethod(cls, "getName");
            Method descMethod = ClassUtil.getDeclaredMethod(cls, "getDescription");
            List<EnumItemVo> enumItemDtoList = new ArrayList<>();
            for(Object obj : constants){
                String enumName = obj.toString();
                if(enumName.equals("DEFAULT")){
                    continue;
                }
                EnumItemVo enumItemDto = new EnumItemVo();
                enumItemDto.setName(enumName);
                int code = (int)codeMethod.invoke(obj);
                String name = (String)nameMethod.invoke(obj);
                String desc = (String)descMethod.invoke(obj);
                enumItemDto.setCode(code);
                enumItemDto.setCname(name);
                enumItemDto.setDescription(desc);
                enumItemDtoList.add(enumItemDto);
            }
            enumDto.setItems(enumItemDtoList.toArray(new EnumItemVo[0]));
            enumsByName.put(enumDto.getCode(),enumDto);
            EnumNodeVo enumNodeDto = new EnumNodeVo(enumDto);
            enumMemoByClas.put(cls,enumNodeDto);

        }
    }

    @Override
    public IEnumVo findByName(String pName) throws ServiceException {
        EnumVo enumDto = enumsByName.get(pName);
        if(null == enumDto){
            SysTypeVo sysTypeVo = mSysTypeMemo.infoFromMemo(pName);
            return sysTypeVo;
        }
        return enumDto;
    }

    @Override
    public EnumNodeVo findByClass(Class pCls) {
        EnumNodeVo enumNodeDto = enumMemoByClas.get(pCls);
        return enumNodeDto;
    }

    @Override
    public EnumVo[] list() {
        EnumVo[] enumDtoArray = enumsByName.values().toArray(new EnumVo[0]);
        return enumDtoArray;
    }

    @Override
    public void addEnumDto(EnumVo pEnumDto) {
        if(!enumsByName.containsKey(pEnumDto.getCode())){
            enumsByName.put(pEnumDto.getCode(),pEnumDto);
        }
    }
}

2.4 使用与演示

2.4.1 类型管理

2.4.1.1 nacos 配置

bailan3-enums-mgr Group DEV

spring:
  application:
    name: bailan3-enums-mgr
  datasource:
    #数据库配置
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://localhost:3306/platform?useUnicode=true&characterEncoding=utf-8
    username: app
    password: ILv0404@1314
    hikari:
      # 连接池最大连接数,默认是10
      maximum-pool-size: 100
      # 最小空闲链接
      minimum-idle: 5
      # 空闲连接存活最大时间,默认 600000(10分钟)
      idle-timeout: 600000
      # 数据库连接超时时间,默认30秒,即30000
      connection-timeout: 30000
      # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟;正在使用的连接永远不会退休,只有在关闭后才会被删除。
      max-lifetime: 1800000
      # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
      auto-commit: true
      pool-name: Hikari
  redis:
    host: localhost
    port: 6379
    password: ilv0404@1314
    # 连接超时时间(记得添加单位,Duration)
    timeout: 10000ms
    lettuce:
      shutdown-timeout: 100 # 关闭超时时间
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
        min-idle: 0 # 连接池中的最小空闲连接
swagger:
  enable: true
  group-name: "类型管理接口"
  api-package: indi.zhifa.recipe.bailan.framework.enums.controller.api
  api-regex: "/api/**"
  title: "类型管理接口"
  description: "类型管理接口"
  version: "1.0.0"
  name: "芝法酱"
  email: "hataksumo@163.com"
  url: "https://github.com/hataksumo"
2.4.1.2 启动配置
server:
  # 服务端口
  port: 8081
spring:
  application:
    name: "bailan3-enums-mgr"
  servlet:
    multipart:
      enabled : true
      max-file-size: "256MB"
      max-request-size: "256MB"
  cloud:
    nacos:
      server-addr: localhost:8848
      config:
        namespace: bailan
        name: bailan3-enums-mgr
        file-extension: yml
        group: DEV
      discovery:
        register-enabled: true
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
deregisterdrivers=true
useprefix=true
excludecategories=info,debug,result,commit,resultset
dateformat=yyyy-MM-dd HH:mm:ss
outagedetection=true
outagedetectioninterval=2
2.4.1.3 pom引用
     <parent>
        <artifactId>busy</artifactId>
        <groupId>indi.zhifa.recipe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>

    <artifactId>enums-mgr</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>indi.zhifa.recipe</groupId>
            <artifactId>framework-web-all</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>indi.zhifa.recipe</groupId>
            <artifactId>framework-enums-mgr</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- ******************lombok****************************-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
2.4.1.4 启动类
@MapperScan(basePackages ={"indi.zhifa.recipe.bailan.framework.**.mapper"})
@SpringBootApplication(
        scanBasePackages = {"indi.zhifa.recipe.bailan.framework","indi.zhifa.recipe.bailan.enumsmgr"}
)
public class EnumsMgrApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(EnumsMgrApplication.class, args);
    }
}
2.4.1.5 MetaObjectHandler
@Component
public class MetaObjectHandler extends BaseMetaObjectHandlerAdapter {
}
2.4.1.6 AppInit

启动加载的代码

@Component
@RequiredArgsConstructor
public class AppInit implements CommandLineRunner {

    private final ITypeMgrService mTypeMgrService;
    @Override
    public void run(String... args) throws Exception {
        DtoEntityUtil.init();
        mTypeMgrService.init();
    }
}
2.4.1.7 测试过程

创建类型
在这里插入图片描述

全量更新类型
在这里插入图片描述

通过code查询
在这里插入图片描述

添加法器类型项
在这里插入图片描述
通过id查询
在这里插入图片描述
测试数据

{
    "code": "gennshinn_major",
    "defaultIdx": -1,
    "defaultItem": "全部",
    "description": "原神职业",
    "name": "原神职业",
    "items": [
      {
        "code": 1,
        "name": "sword",
        "cname": "剑",
        "description": "剑"
      },
       {
        "code": 2,
        "name": "polearm",
        "cname": "长柄武器",
        "description": "长柄武器"
      },
      {
        "code": 3,
        "name": "claymore",
        "cname": "大剑",
        "description": "大剑"
      },
      {
        "code": 4,
        "name": "bow",
        "cname": "弓",
        "description": "弓"
      },
      {
        "code": 5,
        "name": "catalyst",
        "cname": "法器",
        "description": "法器"
      }
    ]
}

2.4.2 枚举的客户端

平时的业务服务器,就相当于枚举的客户端,直接引入framework-enums-client即可

2.4.2.1 pom 引用

<parent>
        <artifactId>bailan</artifactId>
        <groupId>indi.zhifa.recipe</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>enum-client-test</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>indi.zhifa.recipe</groupId>
            <artifactId>framework-web-all</artifactId>
        </dependency>
        <dependency>
            <groupId>indi.zhifa.recipe</groupId>
            <artifactId>framework-enums-client</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- ******************lombok****************************-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

2.4.2.2 启动文件和nacos配置

bailan3-enums-cli DEV

spring:
  application:
    name: bailan3-enums-cli
  datasource:
    #数据库配置
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:mysql://localhost:3306/platform?useUnicode=true&characterEncoding=utf-8
    username: app
    password: ILv0404@1314
    hikari:
      # 连接池最大连接数,默认是10
      maximum-pool-size: 100
      # 最小空闲链接
      minimum-idle: 5
      # 空闲连接存活最大时间,默认 600000(10分钟)
      idle-timeout: 600000
      # 数据库连接超时时间,默认30秒,即30000
      connection-timeout: 30000
      # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟;正在使用的连接永远不会退休,只有在关闭后才会被删除。
      max-lifetime: 1800000
      # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
      auto-commit: true
      pool-name: Hikari
  redis:
    host: localhost
    port: 6379
    password: ilv0404@1314
    # 连接超时时间(记得添加单位,Duration)
    timeout: 10000ms
    lettuce:
      shutdown-timeout: 100 # 关闭超时时间
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
        min-idle: 0 # 连接池中的最小空闲连接
swagger:
  enable: true
  group-name: "类型管理接口"
  api-package: indi.zhifa.recipe.bailan.framework.enums.controller.api
  api-regex: "/api/**"
  title: "类型管理接口"
  description: "类型管理接口"
  version: "1.0.0"
  name: "芝法酱"
  email: "hataksumo@163.com"
  url: "https://github.com/hataksumo"

mybatis-plus:
  typeEnumsPackage: indi.zhifa.recipe.bailan.enumsclient.entity.enums
enum-memo:
  enum-packages:
    - indi.zhifa.recipe.bailan.enumsclient.entity.enums

bootstrap.yml

server:
  # 服务端口
  port: 8082
spring:
  application:
    name: "bailan3-enums-cli"
  servlet:
    multipart:
      enabled : true
      max-file-size: "256MB"
      max-request-size: "256MB"
  cloud:
    nacos:
      server-addr: localhost:8848
      config:
        namespace: bailan
        name: bailan3-enums-cli
        file-extension: yml
        group: DEV
      discovery:
        register-enabled: true

2.4.2.3 启动类

@MapperScan(basePackages ={"indi.zhifa.recipe.bailan.framework.**.mapper"})
@SpringBootApplication(
        scanBasePackages = {"indi.zhifa.recipe.bailan.framework","indi.zhifa.recipe.bailan.enumsclient"}
)
public class EnumClientTestApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(EnumClientTestApplication.class, args);
    }
}

2.4.2.4 测试枚举

package indi.zhifa.recipe.bailan.enumsclient.entity.enums;

@EnumDesc(name = "角色",desc = "角色的枚举")
@RequiredArgsConstructor
public enum ERoles {

    CUSTOMER(0,"客户","普通客户,只有浏览权限"),
    SHOP(1,"商家","商家的枚举,可以创建商品"),
    SYS_ADM(2,"系统管理员","系统管理员,可以编辑系统规则,调用危险接口调整数据"),
    ACCOUNT_ADM(3,"账户管理员","账户管理员,可以对账户进行操作");

    @EnumValue
    @Getter
    final int code;
    @Getter
    final String name;
    @Getter
    final String description;
}
package indi.zhifa.recipe.bailan.enumsclient.entity.enums;

@EnumDesc(name = "电压等级",desc = "电压等级")
@AllArgsConstructor
public enum EVoltage {
    V35(1,"35kV","35千伏"),
    V110(2,"110kV","110千伏"),
    V220(3,"220kV","220千伏"),
    V500(4,"500kV","500千伏"),
    VD800(5,"±800kV","±800千伏"),
    V1000(6,"1000kV","1000千伏");

    @EnumValue
    @Getter
    int code;
    @Getter
    String name;
    @Getter
    String description;
}

2.4.2.5 AppInit

@RequiredArgsConstructor
@Component
public class AppInit implements CommandLineRunner {

    private final IEnumMemoService mEnumMemoService;
    private final EnumMemoConfig mEnumMemoConfig;
    @Override
    public void run(String... args) throws Exception {
        DtoEntityUtil.init();
        mEnumMemoService.initEnum(mEnumMemoConfig.getEnumPackages());
    }
}

2.4.2.6 测试过程

查询电压等级:
在这里插入图片描述

查询原神职业:
在这里插入图片描述

在后端管理服务中,添加一个JLPT等级
在这里插入图片描述
在这里插入图片描述
查询JlptLevel
在这里插入图片描述
删除JLPT等级
在这里插入图片描述
再次查询JLPT等级
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值