源码地址:
https://github.com/g5zhu5896/extension-parent
目录
一、背景
项目开发中实体类少不了一些枚举、字典和url等字段。而在实体中查出这些字段时经常需要一些额外的编码查出如枚举对应的中文描述、字典对应的中文描述、url拼接上ip、端口等前缀。
二、用处:
主要用于减去上面的额外编码,实体类中可以直接配置枚举类、字典类、url等。然后通过本工具自动绑定需要返回的如枚举中文描述的额外信息。对swagger的展示效果也有提供一定的扩展
举例:下面只给出查询的例子,实体类直接配置枚举、字典不会对增删改查产生任何影响
1.枚举
枚举类 UserIsEnableEnum: 插入数据库的值为 对应value字段(1和2)
@Getter
public enum UserIsEnableEnum implements BaseEnum<Integer> {
/**
*
*/
YES(1, "启用"),
NO(2, "未启用");
private Integer value;
private String label;
public static final String description = "是否启用:1-启用 2-未启用";
UserIsEnableEnum(Integer value, String label) {
this.value = value;
this.label = label;
}
}
实体类配置:UserIsEnableEnum isEnable;为枚举字段
@Data
@ApiModel
public class User {
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("name")
private String name;
@ApiModelProperty(UserIsEnableEnum.description)
private UserIsEnableEnum isEnable;
}
结果: 从数据库查出来到返回前端.自动增加了 isEnableLabel字段为中文文本
2.字典
字典类 UserSourceDict :字典类的 value 和 label是存在表里的,而不是写在代码里的(枚举是写在代码里的)
public class UserSourceDict extends DictBean<String> {
/**
* 如果代码需要使用到可以这样创建枚举字典提供使用,尽量不要代码直接字典值
* 字典表是有 jd tx bd 三个字典项的。
*/
public static final UserSourceDict JD = create("tx");
/**
* USER_SOURCE 需要对应字典表的 字典key
*/
public UserSourceDict() {
setDictKey("USER_SOURCE");
}
private static UserSourceDict create(String value) {
return create(value, UserSourceDict.class);
}
}
字典数据
INSERT INTO dict (id, label, value, dict_key)
VALUES (1, '京东', 'jd', 'USER_SOURCE'),
(2, '百度', 'bd', 'USER_SOURCE'),
(3, '腾讯', 'tx', 'USER_SOURCE'),
(4, '男', '1', 'GENDER'),
(5, '女', '2', 'GENDER');
实体类配置:UserSourceDict source 为字典字段
@Data
@ApiModel
public class User {
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("name")
private String name;
@ApiModelProperty("来源")
private UserSourceDict source;
}
结果: 从数据库查出来到返回前端.自动增加了 sourceLabel字段 为中文文本
3.url
实体类配置:String headUrl 字段,并使用序列化器FilePathSerializer。
@Data
@ApiModel
public class User {
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("name")
private String name;
@JsonSerialize(using = FilePathSerializer.class)
@ApiModelProperty("头像")
private String headUrl;
}
数据库数据
INSERT INTO user (id, name, age, is_enable, source, gender, head_url)
VALUES (1, 'Jone', 18, 1, 'jd', 1, '/1.jpg'),
(2, 'Jack', 20, 2, 'bd', 1, '/2.jpg'),
(3, 'Tom', 28, 2, 'tx', 1, '/3.jpg'),
(4, 'Sandy', 21, 2, 'bd', 2, '/4.jpg'),
(5, 'Billie', 24, 1, 'jd', 2, '/5.jpg');
结果: 从数据库查出来到返回前端.自动增加了 fullHeadUrl字段会带上完整ip,前缀ip是配置在配置文件里的
三、详细配置(demo地址 下载直接启动就好)
1.引入 extension-utils-starter.中央仓库中可以直接拉取到依赖
<dependency> <groupId>io.github.g5zhu5896</groupId> <artifactId>extension-utils-starter</artifactId> <version>1.2.0</version> </dependency>
2.DictBean:继承DictBean后就可以有字典相关的扩展.
TableDictBean:可以通过配置sql查询字典
BaseEnum:枚举类实现BaseEnum接口即有枚举相关的扩展
FilePathSerializer:一些上传的图片和文件地址可以增加这个注解,即可额外返回一个全路径字段,拼接的前缀在yml配置中维护//DictBean public class UserSourceDict extends DictBean<String> { public UserSourceDict() { setDictKey("USER_SOURCE"); } } //controller @ApiOperation(value = "列表") @GetMapping("/list2") public List<User> list2(User user) { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq(user.getSource() != null, "source", user.getSource()); //userService.list 方法是 mybatis-plus的ServiceImpl实现的 return userService.list(wrapper); } //entity @Data @ApiModel public class User { ...............................其他字段 @ApiModelProperty(UserIsEnableEnum.description) private UserIsEnableEnum isEnable; @ApiModelProperty("来源") private UserSourceDict source; @JsonSerialize(using = FilePathSerializer.class) @ApiModelProperty("头像") private String headUrl; }
3.实现 AbstractDictService 并注册为Bean。此处注入的dictService 就是从数据库里查询字典用的
@Service public class DictBeanServiceImpl extends AbstractDictService { @Autowired private DictService dictService; public DictBeanServiceImpl() { super(new HttpRequestDictCacheStrategy()); } @Override public List<IDictBean> queryBeanListByDictKey(DictBean baseDictBean) { Class<? extends IDictBean> clazz = baseDictBean.getClass(); List<Dict> dictList = dictService.list(baseDictBean.getDictKey()); Class<?> valueType = ReflectUtils.getSuperClassGenericType(clazz, 0); List<IDictBean> dictBeanList = Lists.newArrayListWithCapacity(dictList.size()); dictList.forEach(dict -> { IDictBean dictBean = baseDictBean.clone(); if (valueType.isAssignableFrom(Integer.class)) { dictBean.setValue(Integer.parseInt(dict.getValue())); } else { dictBean.setValue(dict.getValue()); } dictBean.setLabel(dict.getLabel()); dictBeanList.add(dictBean); }); return dictBeanList; } @Override public List<IDictBean> queryBeanListBySql(TableDictBean baseDictBean) { Class<? extends TableDictBean> clazz = baseDictBean.getClass(); List<Map<String, Object>> dictList = dictService.selectBySql(baseDictBean.getSql()); Class<?> valueType = ReflectUtils.getSuperClassGenericType(clazz, 0); List<IDictBean> dictBeanList = Lists.newArrayListWithCapacity(dictList.size()); dictList.forEach(dict -> { IDictBean dictBean = baseDictBean.clone(); Object value = dict.get("VALUE"); if (valueType.isAssignableFrom(Integer.class)) { dictBean.setValue(Integer.parseInt(value.toString())); } else { dictBean.setValue(value.toString()); } dictBean.setLabel(dict.get("LABEL").toString()); dictBeanList.add(dictBean); }); return dictBeanList; } }
4.yml配置easy: dictBeanPackage: com.easy.demo.dict,com.easy.demo.entity fileUrlPrefix: http://localhost:8080 dictSwaggerSupport: true #下面四个可以不配置,目前配置的就是默认值 fileFieldPrefix: full fileFieldSuffix: enumFieldPrefix: enumFieldSuffix: Label dictFieldPrefix: dictFieldSuffix: Label
四、扩展:
1.DictCacheStrategy:缓存策略.
目前默认使用的是 HttpRequestDictCacheStrategy 策略,针对同一次请求会缓存查询过的字典,不会重复查询数据库,但是跨请求就需要从数据库查询。
可以实现自己的策略,如缓存到redis中。
五、配置:yml 里的 easy 配置介绍
easy:
dictBeanPackage: com.easy.demo.dict,com.easy.demo.entity
fileUrlPrefix: http://localhost:8080
dictSwaggerSupport: true
#下面四个可以不配置,目前配置的就是默认值
fileFieldPrefix: full
fileFieldSuffix:
enumFieldPrefix:
enumFieldSuffix: Label
dictFieldPrefix:
dictFieldSuffix: Label
1.dictBeanPackage: com.easy.demo.dict,com.easy.demo.entity
配置DictBean子类所在的包,需要扫描到DictBean类注册mybatis的TypeHandler。没配置mybaits查询和存储的时候就没法自动转换。
2.dictSwaggerSupport: true 没配置默认是false.
直接上效果差异吧,左边的是false的效果,右边的是true的效果,两种执行都是可以正常绑定的。
如果只看效果应该是配置为true好点。但是swagger的扩展对这个的支持不太好,目前在2.9.2里面的实现是通过反射获取私有属性修改的。
3.FilePathSerializer相关的配置,
fileUrlPrefix: http://localhost:8080 文件url链接拼接的前缀
fileFieldPrefix: full 文件url字段的前缀,可不配置
fileFieldSuffix: Path 文件url字段的后缀,有默认值假设有个字段 headUrl=/img/head.png ,则如上配置增加的额外字段结果为: fullHeadUrlPath=http://localhost:8080/img/head.png
4.字典字段的前缀后缀
dictFieldPrefix: 字典字段的前缀,可不配置
dictFieldSuffix: Label 字典字段的后缀.可不配置
5.枚举字段的前缀后缀
enumFieldPrefix: 枚举字段的前缀,可不配置
enumFieldSuffix: Label 模具字段的后缀,可不配置
六:可以优化的。
目前在swagger文档中是不会展示扩展的额外字段(如fullHeadUrl)的。这个要扩展起来比较麻烦,为了文档展示额外字段搞那么多个人觉得没太大必要。
有需要的话可以参考 GitHub - g5zhu5896/swagger-group-demo: swagger api文档 可以用同一个model根据group在不同api里展示不同字段和描述的实现。实现方法类似,都是往ModelMap增加额外的Model,然后修改返回的Response 的 ModelRef。