ソースを参照

增加字典功能

zhouhao 7 年 前
コミット
c4c571fc98
19 ファイル変更633 行追加1 行削除
  1. 12 0
      hsweb-core/pom.xml
  2. 1 1
      hsweb-core/src/main/java/org/hswebframework/web/convert/CustomMessageConverter.java
  3. 9 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/ClassDictDefine.java
  4. 57 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/Dict.java
  5. 21 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/DictDefine.java
  6. 13 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/DictDefineRepository.java
  7. 13 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/DictParser.java
  8. 18 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/DictSupportApi.java
  9. 31 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/Item.java
  10. 18 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/ItemDefine.java
  11. 27 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultClassDictDefine.java
  12. 27 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictDefine.java
  13. 93 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictDefineRepository.java
  14. 36 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictParser.java
  15. 118 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictSupportApi.java
  16. 24 0
      hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultItemDefine.java
  17. 69 0
      hsweb-core/src/test/java/org/hswebframework/web/dict/DictDefineTest.java
  18. 25 0
      hsweb-core/src/test/java/org/hswebframework/web/dict/UseDictEntity.java
  19. 21 0
      hsweb-core/src/test/java/org/hswebframework/web/dict/UseDictEntity2.java

+ 12 - 0
hsweb-core/pom.xml

@@ -17,5 +17,17 @@
             <groupId>org.hswebframework</groupId>
             <artifactId>hsweb-utils</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+        </dependency>
     </dependencies>
 </project>

+ 1 - 1
hsweb-core/src/main/java/org/hswebframework/web/convert/CustomMessageConverter.java

@@ -2,7 +2,7 @@ package org.hswebframework.web.convert;
 
 /**
  * @author zhouhao
- * @since
+ * @since 3.0
  */
 public interface CustomMessageConverter {
     boolean support(Class clazz);

+ 9 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/ClassDictDefine.java

@@ -0,0 +1,9 @@
+package org.hswebframework.web.dict;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface ClassDictDefine extends DictDefine {
+    String getField();
+}

+ 57 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/Dict.java

@@ -0,0 +1,57 @@
+package org.hswebframework.web.dict;
+
+import org.hswebframework.web.dict.defaults.DefaultDictParser;
+
+import java.lang.annotation.*;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Dict {
+    /**
+     * 如果id对应的字典已经存在,则设置此属性进行关联,除了{@link Dict#parserId}属性的其他属性将被忽略<br>
+     * 如果id对应的字典不存在,则将使用其他属性进行定义
+     *
+     * @return 字典ID
+     * @see DictDefine#getId()
+     * @see DictDefineRepository
+     */
+    String id() default "";
+
+    /**
+     * 字典别名,如果指定了别名:
+     * <ul>
+     * <li>在序列化为json后,会添加一个字段到json中.
+     * 字段的名称为此属性的值,字段的值为{@link Dict#parserId}对应的解析器的解析结果</li>
+     * <li>在反序列化json时,如果被注解的字段值为null,将尝试解析别名字段的值并设置到注解的字段</li>
+     * </ul>
+     *
+     * @return 别名
+     */
+    String alias() default "";
+
+    /**
+     * @return 字典说明, 备注
+     */
+    String comments() default "";
+
+    /**
+     * 字典解析器ID
+     *
+     * @return 字典解析器的id, 默认为default, 如果对应的解析器不存在,也将使用default
+     * @see DictParser
+     * @see DefaultDictParser
+     */
+    String parserId() default "default";
+
+    /**
+     * 如果要直接在注解上定义字典,通过设置此属性进行定义
+     *
+     * @return 字典选项
+     */
+    Item[] items() default {};
+}

+ 21 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/DictDefine.java

@@ -0,0 +1,21 @@
+package org.hswebframework.web.dict;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface DictDefine extends Serializable {
+    String getId();
+
+    String getAlias();
+
+    String getComments();
+
+    String getParserId();
+
+    List<ItemDefine> getItems();
+
+}

+ 13 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/DictDefineRepository.java

@@ -0,0 +1,13 @@
+package org.hswebframework.web.dict;
+
+import java.util.List;
+
+/**
+ * @author zhouhao
+ * @since 1.0
+ */
+public interface DictDefineRepository {
+    DictDefine getDefine(String id);
+
+    List<ClassDictDefine> getDefine(Class type);
+}

+ 13 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/DictParser.java

@@ -0,0 +1,13 @@
+package org.hswebframework.web.dict;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface DictParser {
+    String getId();
+
+    String parseText(DictDefine dictDefine, String value);
+
+    String parseValue(DictDefine dictDefine, String text);
+}

+ 18 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/DictSupportApi.java

@@ -0,0 +1,18 @@
+package org.hswebframework.web.dict;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface DictSupportApi {
+    DictParser getParser(String id, String defaultId);
+
+    default DictParser getParser(String id) {
+        return getParser(id, "default");
+    }
+
+    <T> T wrap(T target);
+
+    <T> T unwrap(T target);
+
+}

+ 31 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/Item.java

@@ -0,0 +1,31 @@
+package org.hswebframework.web.dict;
+
+import java.lang.annotation.*;
+
+/**
+ * 字典选项注解
+ *
+ * @author zhouhao
+ * @see Dict
+ * @since 3.0
+ */
+@Target({ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Item {
+
+    /**
+     * @return 选项文本, 如: 男
+     */
+    String text() default "";
+
+    /**
+     * @return 选项值, 如: 1
+     */
+    String value() default "";
+
+    /**
+     * @return 字典说明
+     */
+    String[] comments() default {};
+}

+ 18 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/ItemDefine.java

@@ -0,0 +1,18 @@
+package org.hswebframework.web.dict;
+
+import java.util.List;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public interface ItemDefine {
+    String getText();
+
+    String getValue();
+
+    String getComments();
+
+    List<ItemDefine> getChildren();
+
+}

+ 27 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultClassDictDefine.java

@@ -0,0 +1,27 @@
+package org.hswebframework.web.dict.defaults;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hswebframework.web.dict.ClassDictDefine;
+import org.hswebframework.web.dict.ItemDefine;
+
+import java.util.List;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class DefaultClassDictDefine implements ClassDictDefine {
+    private String           field;
+    private String           id;
+    private String           alias;
+    private String           comments;
+    private String           parserId;
+    private List<ItemDefine> items;
+}

+ 27 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictDefine.java

@@ -0,0 +1,27 @@
+package org.hswebframework.web.dict.defaults;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hswebframework.web.dict.ClassDictDefine;
+import org.hswebframework.web.dict.DictDefine;
+import org.hswebframework.web.dict.ItemDefine;
+
+import java.util.List;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class DefaultDictDefine implements DictDefine {
+    private String           id;
+    private String           alias;
+    private String           comments;
+    private String           parserId;
+    private List<ItemDefine> items;
+}

+ 93 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictDefineRepository.java

@@ -0,0 +1,93 @@
+package org.hswebframework.web.dict.defaults;
+
+import org.hswebframework.web.dict.*;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public class DefaultDictDefineRepository implements DictDefineRepository {
+    protected Map<String, DictDefine> parsedDict = new HashMap<>();
+
+    public void registerDefine(DictDefine define) {
+        parsedDict.put(define.getId(), define);
+    }
+
+    @Override
+    public DictDefine getDefine(String id) {
+        return parsedDict.get(id);
+    }
+
+    private List<Field> parseField(Class type) {
+        if (type == Object.class) {
+            return Collections.emptyList();
+        }
+        List<Field> lst = new ArrayList<>();
+        lst.addAll(Arrays.asList(type.getDeclaredFields()));
+        lst.addAll(parseField(type.getSuperclass()));
+
+        return lst;
+    }
+
+    @Override
+    public List<ClassDictDefine> getDefine(Class type) {
+        return this.parseDefine(type);
+    }
+
+    protected List<ClassDictDefine> parseDefine(Class type) {
+        List<ClassDictDefine> defines = new ArrayList<>();
+
+        for (Field field : parseField(type)) {
+            Dict dict = field.getAnnotation(Dict.class);
+            if (dict == null) {
+                continue;
+            }
+            String id = dict.id();
+            DictDefine dictDefine = getDefine(id);
+            if (dictDefine instanceof ClassDictDefine) {
+                defines.add(((ClassDictDefine) dictDefine));
+            } else {
+                DefaultClassDictDefine define;
+                if (dictDefine != null) {
+                    List<ItemDefine> items = dictDefine.getItems()
+                            .stream()
+                            .map(item -> DefaultItemDefine.builder()
+                                    .text(item.getText())
+                                    .value(item.getValue())
+                                    .comments(String.join(",", item.getComments()))
+                                    .build())
+                            .collect(Collectors.toList());
+                    define = DefaultClassDictDefine.builder()
+                            .id(id)
+                            .alias(dictDefine.getAlias())
+                            .comments(dictDefine.getComments())
+                            .field(field.getName())
+                            .items(items)
+                            .build();
+
+                } else {
+                    List<ItemDefine> items = Arrays
+                            .stream(dict.items())
+                            .map(item -> DefaultItemDefine.builder()
+                                    .text(item.text())
+                                    .value(item.value())
+                                    .comments(String.join(",", item.comments()))
+                                    .build()).collect(Collectors.toList());
+                    define = DefaultClassDictDefine.builder()
+                            .id(id)
+                            .alias(dict.alias())
+                            .comments(dict.comments())
+                            .field(field.getName())
+                            .items(items)
+                            .build();
+                }
+                defines.add(define);
+            }
+        }
+        return defines;
+    }
+}

+ 36 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictParser.java

@@ -0,0 +1,36 @@
+package org.hswebframework.web.dict.defaults;
+
+import org.hswebframework.web.dict.DictDefine;
+import org.hswebframework.web.dict.DictParser;
+import org.hswebframework.web.dict.ItemDefine;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public class DefaultDictParser implements DictParser {
+    @Override
+    public String getId() {
+        return "default";
+    }
+
+    @Override
+    public String parseText(DictDefine dictDefine, String value) {
+        return dictDefine.getItems()
+                .stream()
+                .filter(itemDefine -> itemDefine.getValue().equals(value))
+                .map(ItemDefine::getText)
+                .findFirst()
+                .orElse(value);
+    }
+
+    @Override
+    public String parseValue(DictDefine dictDefine, String text) {
+        return dictDefine.getItems()
+                .stream()
+                .filter(itemDefine -> itemDefine.getText().equals(text))
+                .map(ItemDefine::getValue)
+                .findFirst()
+                .orElse(text);
+    }
+}

+ 118 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultDictSupportApi.java

@@ -0,0 +1,118 @@
+package org.hswebframework.web.dict.defaults;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.beanutils.BeanUtils;
+import org.hswebframework.web.dict.ClassDictDefine;
+import org.hswebframework.web.dict.DictDefineRepository;
+import org.hswebframework.web.dict.DictParser;
+import org.hswebframework.web.dict.DictSupportApi;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+@Slf4j
+public class DefaultDictSupportApi implements DictSupportApi {
+
+    private DictDefineRepository repository;
+
+    private Map<String, DictParser> parserRepo = new HashMap<>();
+
+    public DefaultDictSupportApi(DictDefineRepository repository) {
+        this.repository = repository;
+        DictParser defaultParser = new DefaultDictParser();
+        parserRepo.put(defaultParser.getId(), defaultParser);
+    }
+
+    @Override
+    public DictParser getParser(String id, String defaultId) {
+        return Optional.ofNullable(parserRepo.get(id)).orElseGet(() -> parserRepo.get(defaultId));
+    }
+
+    @Override
+    public <T> T unwrap(T target) {
+        if (target == null) {
+            return null;
+        }
+        if (target instanceof Map) {
+            return target;
+        }
+        if (target instanceof List) {
+            return (T) ((List) target).stream()
+                    .map(this::wrap)
+                    .collect(Collectors.toList());
+        }
+        Class type = ClassUtils.getUserClass(target);
+        List<ClassDictDefine> defines = repository.getDefine(type);
+        if (defines.isEmpty()) {
+            return target;
+        }
+        for (ClassDictDefine define : defines) {
+            String fieldName = define.getField();
+            String alias = define.getAlias();
+            if (StringUtils.isEmpty(alias)) {
+                continue;
+            }
+            try {
+                Object fieldValue = BeanUtils.getProperty(target, fieldName);
+                if (fieldValue != null) {
+                    continue;
+                }
+                Object value = BeanUtils.getProperty(target, alias);
+                if (value == null) {
+                    continue;
+                }
+                BeanUtils.setProperty(target, fieldName, getParser(define.getParserId()).parseValue(define, String.valueOf(value)));
+            } catch (Exception e) {
+                log.warn("wrap error", e.getMessage());
+            }
+        }
+        return target;
+    }
+
+    @Override
+    @SuppressWarnings("all")
+    public <T> T wrap(T target) {
+        if (target == null) {
+            return null;
+        }
+        if (target instanceof Map) {
+            return target;
+        }
+        if (target instanceof List) {
+            return (T) ((List) target).stream().map(this::wrap).collect(Collectors.toList());
+        }
+        Class type = ClassUtils.getUserClass(target);
+        List<ClassDictDefine> defines = repository.getDefine(type);
+        if (defines.isEmpty()) {
+            return target;
+        }
+        for (ClassDictDefine define : defines) {
+            String fieldName = define.getField();
+            String alias = define.getAlias();
+            if (StringUtils.isEmpty(alias)) {
+                continue;
+            }
+            try {
+                Object value = BeanUtils.getProperty(target, fieldName);
+                if (value == null) {
+                    continue;
+                }
+                BeanUtils.setProperty(target, alias, getParser(define.getParserId()).parseText(define, String.valueOf(value)));
+            } catch (Exception e) {
+                log.warn("wrap error", e.getMessage());
+            }
+        }
+        return target;
+    }
+}

+ 24 - 0
hsweb-core/src/main/java/org/hswebframework/web/dict/defaults/DefaultItemDefine.java

@@ -0,0 +1,24 @@
+package org.hswebframework.web.dict.defaults;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hswebframework.web.dict.ItemDefine;
+
+import java.util.List;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class DefaultItemDefine implements ItemDefine {
+    private String           text;
+    private String           value;
+    private String           comments;
+    private List<ItemDefine> children;
+}

+ 69 - 0
hsweb-core/src/test/java/org/hswebframework/web/dict/DictDefineTest.java

@@ -0,0 +1,69 @@
+package org.hswebframework.web.dict;
+
+import org.hswebframework.web.dict.defaults.DefaultClassDictDefine;
+import org.hswebframework.web.dict.defaults.DefaultDictDefineRepository;
+import org.hswebframework.web.dict.defaults.DefaultDictSupportApi;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+public class DictDefineTest {
+
+    private DefaultDictDefineRepository repository = new DefaultDictDefineRepository();
+
+    private DictSupportApi api = new DefaultDictSupportApi(repository);
+
+    @Test
+    public void testParse() {
+
+        DefaultClassDictDefine define = DefaultClassDictDefine.builder()
+                .id("test-code")
+                .field("code")
+                .build();
+        repository.registerDefine(define);
+        List<ClassDictDefine> defines = repository.getDefine(UseDictEntity2.class);
+        assertFalse(defines.isEmpty());
+        assertEquals(defines.size(), 2);
+    }
+
+    @Test
+    public void testWrap() {
+        assertNull(api.wrap(null));
+        assertNotNull(api.wrap(new HashMap<>()));
+        assertNull(api.unwrap(null));
+        assertNotNull(api.unwrap(new HashMap<>()));
+
+        UseDictEntity2 entity = new UseDictEntity2();
+        entity.setStatus(new Integer(1).byteValue());
+
+        entity = api.wrap(entity);
+
+        assertEquals(entity.getStatusText(), "正常");
+
+        entity.setStatus(null);
+        entity = api.unwrap(entity);
+        assertEquals(entity.getStatus(), Byte.valueOf((byte) 1));
+
+        entity.setStatus((byte) 2);
+        entity = api.unwrap(entity);
+        assertEquals(entity.getStatus(), Byte.valueOf((byte) 2));
+
+        entity.setStatus(null);
+        entity.setStatusText(null);
+        entity.setCode("1");
+        api.wrap(entity);
+
+        assertNull(entity.getStatusText());
+        assertEquals(entity.getCode(), "1");
+
+        api.unwrap(entity);
+        assertEquals(entity.getCode(), "1");
+    }
+}

+ 25 - 0
hsweb-core/src/test/java/org/hswebframework/web/dict/UseDictEntity.java

@@ -0,0 +1,25 @@
+package org.hswebframework.web.dict;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class UseDictEntity {
+    @Dict(
+            alias = "statusText"
+            , items = {
+            @Item(text = "正常", value = "1"),
+            @Item(text = "失效", value = "0")
+    })
+    private Byte status;
+
+    private String statusText;
+}

+ 21 - 0
hsweb-core/src/test/java/org/hswebframework/web/dict/UseDictEntity2.java

@@ -0,0 +1,21 @@
+package org.hswebframework.web.dict;
+
+import lombok.*;
+
+/**
+ * @author zhouhao
+ * @since 3.0
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class UseDictEntity2 extends UseDictEntity {
+    @Dict(id = "test-code",
+            items = {
+                    @Item(text = "编码1", value = "1"),
+                    @Item(text = "编码2", value = "2")
+            })
+    private String code;
+}