From 097d83cabbf881388156a7ebc72e9786d3f4f30d Mon Sep 17 00:00:00 2001 From: HouKunLin Date: Sun, 12 Dec 2021 22:20:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=A0=91=E5=BD=A2?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E6=95=B0=E6=8D=AE=E7=9A=84=E5=AD=97=E5=85=B8?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E8=BD=AC=E6=8D=A2=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../system/dict/starter/DictUtil.java | 66 +++++++++++++++++ .../system/dict/starter/bean/DictTypeVo.java | 14 ++++ .../system/dict/starter/bean/DictValueVo.java | 15 ++++ .../system/dict/starter/json/DictText.java | 11 +++ .../json/DictTextJsonSerializerDefault.java | 17 +++++ .../system/dict/starter/store/DictStore.java | 10 +++ .../dict/starter/store/LocalDictStore.java | 14 ++++ .../dict/starter/store/RedisDictStore.java | 21 +++++- .../system/dict/starter/TreeDataTest.java | 73 +++++++++++++++++++ 10 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/houkunlin/system/dict/starter/TreeDataTest.java diff --git a/build.gradle b/build.gradle index ba28861..bd0fd41 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ plugins { } group = 'com.houkunlin' -version = '1.4.5.1' +version = '1.4.6-BUILD-SNAPSHOT' sourceCompatibility = '1.8' description = """ 系统数据字典自动翻译成字典文本。可集合系统数据库中存储的用户数据字典,也可使用枚举做系统数据字典,主要用在返回数据给前端时自动把字典值翻译成字典文本信息; diff --git a/src/main/java/com/houkunlin/system/dict/starter/DictUtil.java b/src/main/java/com/houkunlin/system/dict/starter/DictUtil.java index 3668e60..851b732 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/DictUtil.java +++ b/src/main/java/com/houkunlin/system/dict/starter/DictUtil.java @@ -19,6 +19,7 @@ public class DictUtil { public static final String TYPE_PREFIX = "dict:t:"; public static final String VALUE_PREFIX = "dict:v:"; + public static final String PARENT_PREFIX = "dict:p:"; private static DictStore store; /** @@ -42,6 +43,13 @@ public static DictTypeVo getDictType(String type) { return store.getDictType(type); } + /** + * 获取字典文本 + * + * @param type 字典类型 + * @param value 字典值 + * @return + */ public static String getDictText(String type, String value) { if (type == null || value == null || store == null) { return null; @@ -69,6 +77,41 @@ public static String getDictText(String type, String value) { return dictText; } + /** + * 获取字典父级值 + * + * @param type 字典类型 + * @param value 字典值 + * @return 字典父级值 + * @since 1.4.6 + */ + public static String getDictParentValue(String type, String value) { + if (type == null || value == null || store == null) { + return null; + } + if (cache == null || missCache == null) { + return store.getDictParentValue(type, value); + } + final String dictParentKey = dictParentKey(type, value); + final String result = cache.getIfPresent(dictParentKey); + if (result != null) { + return result; + } + final AtomicInteger integer = missCache.get(dictParentKey, s -> new AtomicInteger(1)); + if (integer.get() > missNum) { + return null; + } + + final String parentValue = store.getDictParentValue(type, value); + if (parentValue == null) { + // 未命中数据 + integer.incrementAndGet(); + } else { + cache.put(dictParentKey, parentValue); + } + return parentValue; + } + public static String dictKey(String type) { return TYPE_PREFIX + type; } @@ -77,7 +120,30 @@ public static String dictKey(DictValueVo value) { return VALUE_PREFIX + value.getDictType() + ":" + value.getValue(); } + /** + * 构建字典父级值缓存 KEY + * + * @param value 字典值对象 + * @return 字典父级值缓存 KEY + * @since 1.4.6 + */ + public static String dictParentKey(DictValueVo value) { + return PARENT_PREFIX + value.getDictType() + ":" + value.getValue(); + } + public static String dictKey(String type, Object value) { return VALUE_PREFIX + type + ":" + value; } + + /** + * 构建字典父级值缓存 KEY + * + * @param type 字典类型 + * @param value 字典值 + * @return 字典父级值缓存 KEY + * @since 1.4.6 + */ + public static String dictParentKey(String type, Object value) { + return PARENT_PREFIX + type + ":" + value; + } } diff --git a/src/main/java/com/houkunlin/system/dict/starter/bean/DictTypeVo.java b/src/main/java/com/houkunlin/system/dict/starter/bean/DictTypeVo.java index c5c4f9c..baadbf9 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/bean/DictTypeVo.java +++ b/src/main/java/com/houkunlin/system/dict/starter/bean/DictTypeVo.java @@ -96,6 +96,20 @@ public DictTypeVo.DictTypeBuilder add(final Object value, final String title) { return this; } + /** + * 树形结构数据 + * + * @param parentValue 字典父级值 + * @param value 字典值 + * @param title 字典文本 + * @return this + * @since 1.4.6 + */ + public DictTypeVo.DictTypeBuilder add(final Object parentValue, final Object value, final String title) { + this.children.add(new DictValueVo(type, parentValue, value, title, 0)); + return this; + } + public DictTypeVo build() { return new DictTypeVo(title, type, remark, children); } diff --git a/src/main/java/com/houkunlin/system/dict/starter/bean/DictValueVo.java b/src/main/java/com/houkunlin/system/dict/starter/bean/DictValueVo.java index a56e039..2b31040 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/bean/DictValueVo.java +++ b/src/main/java/com/houkunlin/system/dict/starter/bean/DictValueVo.java @@ -29,6 +29,14 @@ public class DictValueVo implements Serializable { @ApiModelProperty(value = "字典类型代码", hidden = true) @JsonIgnore private String dictType; + /** + * 父级字典值,由父级字典值可以组成一个类似树形结构数据的字典信息。 + * 构建树形结构字典数据所需要的一个父级值; + * + * @since 1.4.6 + */ + @ApiModelProperty("父级字典值") + private Object parentValue; /** * 字典值 */ @@ -45,4 +53,11 @@ public class DictValueVo implements Serializable { */ @ApiModelProperty("排序值(系统不会执行排序后再返回给前端)") private int sorted; + + public DictValueVo(final String dictType, final Object value, final String title, final int sorted) { + this.dictType = dictType; + this.value = value; + this.title = title; + this.sorted = sorted; + } } diff --git a/src/main/java/com/houkunlin/system/dict/starter/json/DictText.java b/src/main/java/com/houkunlin/system/dict/starter/json/DictText.java index 37318ce..11fffbe 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/json/DictText.java +++ b/src/main/java/com/houkunlin/system/dict/starter/json/DictText.java @@ -85,6 +85,17 @@ */ Array array() default @Array(split = ""); + /** + * 是否是树形结构数据; + * + * @return boolean + * @since 1.4.6 + */ + boolean tree() default false; + enum Type { /** * 根据全局参数决定配置 diff --git a/src/main/java/com/houkunlin/system/dict/starter/json/DictTextJsonSerializerDefault.java b/src/main/java/com/houkunlin/system/dict/starter/json/DictTextJsonSerializerDefault.java index 1387337..2e30148 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/json/DictTextJsonSerializerDefault.java +++ b/src/main/java/com/houkunlin/system/dict/starter/json/DictTextJsonSerializerDefault.java @@ -228,6 +228,23 @@ private Object obtainResult(final List dictTexts) { * @return 字典值文本 */ protected String obtainDictValueText(String dictValue) { + // @since 1.4.6 - START + if (dictText.tree()) { + final List values = new LinkedList<>(); + String value = dictValue; + do { + final String text = DictUtil.getDictText(dictType, value); + if (text != null) { + values.add(0, text); + } + value = DictUtil.getDictParentValue(dictType, value); + } while (value != null); + if (values.isEmpty()) { + return null; + } + return String.join("/", values); + } + // @since 1.4.6 - END return DictUtil.getDictText(dictType, dictValue); } diff --git a/src/main/java/com/houkunlin/system/dict/starter/store/DictStore.java b/src/main/java/com/houkunlin/system/dict/starter/store/DictStore.java index 081ded7..9476115 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/store/DictStore.java +++ b/src/main/java/com/houkunlin/system/dict/starter/store/DictStore.java @@ -62,4 +62,14 @@ public interface DictStore { * @return 字典文本 */ String getDictText(String type, String value); + + /** + * 获取字典父级值 + * + * @param type 字典所属类型 + * @param value 字典值 + * @return 字典文本 + * @since 1.4.6 + */ + String getDictParentValue(String type, String value); } diff --git a/src/main/java/com/houkunlin/system/dict/starter/store/LocalDictStore.java b/src/main/java/com/houkunlin/system/dict/starter/store/LocalDictStore.java index 0a47ddf..e949089 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/store/LocalDictStore.java +++ b/src/main/java/com/houkunlin/system/dict/starter/store/LocalDictStore.java @@ -47,6 +47,15 @@ public void store(final Iterator iterator) { } } else { CACHE_TEXT.put(dictKey, title); + // @since 1.4.6 - START + final String dictParentKey = DictUtil.dictParentKey(valueVo); + final Object parentValue = valueVo.getParentValue(); + if (parentValue == null) { + CACHE_TEXT.remove(dictParentKey); + } else { + CACHE_TEXT.put(dictParentKey, parentValue.toString()); + } + // @since 1.4.6 - END } }); } @@ -91,6 +100,11 @@ public String getDictText(final String type, final String value) { return remoteDict.getDictText(type, value); } + @Override + public String getDictParentValue(final String type, final String value) { + return CACHE_TEXT.get(DictUtil.dictParentKey(type, value)); + } + @Override public void afterPropertiesSet() throws Exception { if (logger.isDebugEnabled()) { diff --git a/src/main/java/com/houkunlin/system/dict/starter/store/RedisDictStore.java b/src/main/java/com/houkunlin/system/dict/starter/store/RedisDictStore.java index 981af18..27fcfb1 100644 --- a/src/main/java/com/houkunlin/system/dict/starter/store/RedisDictStore.java +++ b/src/main/java/com/houkunlin/system/dict/starter/store/RedisDictStore.java @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; import java.util.Iterator; import java.util.List; @@ -38,6 +39,7 @@ public void store(final DictTypeVo dictType) { @Override public void store(final Iterator iterator) { + final ValueOperations opsForValue = dictValueRedisTemplate.opsForValue(); iterator.forEachRemaining(valueVo -> { final String dictKey = DictUtil.dictKey(valueVo); final String title = valueVo.getTitle(); @@ -47,7 +49,16 @@ public void store(final Iterator iterator) { logger.debug("[removeDictValue] 字典值文本被删除 {}", dictKey); } } else { - dictValueRedisTemplate.opsForValue().set(dictKey, title); + opsForValue.set(dictKey, title); + // @since 1.4.6 - START + final String dictParentKey = DictUtil.dictParentKey(valueVo); + final Object parentValue = valueVo.getParentValue(); + if (parentValue == null) { + dictValueRedisTemplate.delete(dictParentKey); + } else { + opsForValue.set(dictParentKey, parentValue.toString()); + } + // @since 1.4.6 - END } }); } @@ -101,6 +112,14 @@ public String getDictText(final String type, final String value) { return remoteDict.getDictText(type, value); } + @Override + public String getDictParentValue(final String type, final String value) { + if (type == null || value == null) { + return null; + } + return dictValueRedisTemplate.opsForValue().get(DictUtil.dictParentKey(type, value)); + } + @Override public void afterPropertiesSet() throws Exception { if (logger.isDebugEnabled()) { diff --git a/src/test/java/com/houkunlin/system/dict/starter/TreeDataTest.java b/src/test/java/com/houkunlin/system/dict/starter/TreeDataTest.java new file mode 100644 index 0000000..614f1e8 --- /dev/null +++ b/src/test/java/com/houkunlin/system/dict/starter/TreeDataTest.java @@ -0,0 +1,73 @@ +package com.houkunlin.system.dict.starter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.houkunlin.system.dict.starter.bean.DictTypeVo; +import com.houkunlin.system.dict.starter.json.Array; +import com.houkunlin.system.dict.starter.json.DictText; +import com.houkunlin.system.dict.starter.notice.RefreshDictTypeEvent; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationEventPublisher; + +/** + * 默认注解使用测试 + * + * @author HouKunLin + * @since 1.4.6 + */ +@SpringBootTest +class TreeDataTest { + public static final String DICT_TYPE = "TreeData"; + @Autowired + private ObjectMapper objectMapper; + @Autowired + private static ApplicationEventPublisher publisher; + + @Autowired + public void setPublisher(final ApplicationEventPublisher publisher) { + final DictTypeVo typeVo = DictTypeVo.newBuilder(DICT_TYPE, "树形结构数据测试") + .add("", "1", "节点1") + .add("", "2", "节点2") + .add("", "3", "节点3") + .add("1", "1-1", "节点1-1") + .add("1", "1-2", "节点1-2") + .add("1", "1-3", "节点1-3") + .add("2", "2-1", "节点2-1") + .add("2", "2-2", "节点2-2") + .add("2", "2-3", "节点2-3") + .add("3", "3-1", "节点3-1") + .add("3", "3-2", "节点3-2") + .add("3", "3-3", "节点3-3") + .build(); + publisher.publishEvent(new RefreshDictTypeEvent(typeVo)); + } + + /** + * 基础测试 + * + * @throws JsonProcessingException 序列化异常 + * @since 1.4.6 + */ + @Test + void testBasic1() throws JsonProcessingException { + @Data + @AllArgsConstructor + class Bean { + @DictText(value = DICT_TYPE, tree = true) + private String userType; + @DictText(value = DICT_TYPE, tree = true) + private String userType1; + @DictText(value = DICT_TYPE, tree = true, array = @Array(toText = false)) + private String userType3; + } + final Bean bean = new Bean("1", "3-3", "1-1,1-2,1-3,2-1,2-2,2-3,3-1,3-2,3-3,3-4"); + final String value = objectMapper.writeValueAsString(bean); + System.out.println(bean); + System.out.println(value); + } + +}